blob: 3c83dd24d4ca116e808014ffe0c007e69ef37c60 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/capture/video/mac/sample_buffer_transformer_mac.h"
#include <tuple>
#include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
#include "build/build_config.h"
#include "media/capture/video/mac/test/pixel_buffer_test_utils_mac.h"
#include "media/capture/video/mac/video_capture_device_avfoundation_utils_mac.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/libyuv/include/libyuv/convert.h"
namespace media {
namespace {
// Example single colored .jpg file (created with MSPaint). It is of RGB color
// (255, 127, 63).
const uint8_t kExampleJpegData[] = {
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
0x01, 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
0x00, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x04,
0x04, 0x03, 0x05, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x08,
0x09, 0x0b, 0x09, 0x08, 0x08, 0x0a, 0x08, 0x07, 0x07, 0x0a, 0x0d, 0x0a,
0x0a, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x07, 0x09, 0x0e, 0x0f, 0x0d, 0x0c,
0x0e, 0x0b, 0x0c, 0x0c, 0x0c, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x02, 0x02,
0x02, 0x03, 0x03, 0x03, 0x06, 0x03, 0x03, 0x06, 0x0c, 0x08, 0x07, 0x08,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x10, 0x00, 0x20, 0x03,
0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00,
0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00,
0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00,
0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24,
0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86,
0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,
0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00,
0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00,
0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00,
0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08,
0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15,
0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55,
0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84,
0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4,
0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00,
0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf7,
0x8a, 0x28, 0xa2, 0xbf, 0x89, 0xcf, 0xf4, 0x50, 0x28, 0xa2, 0x8a, 0x00,
0xff, 0xd9};
constexpr size_t kExampleJpegDataSize = 638;
constexpr uint32_t kExampleJpegWidth = 32;
constexpr uint32_t kExampleJpegHeight = 16;
constexpr uint32_t kExampleJpegScaledDownWidth = 16;
constexpr uint32_t kExampleJpegScaledDownHeight = 8;
const uint8_t kInvalidJpegData[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
constexpr size_t kInvalidJpegDataSize = 24;
constexpr uint8_t kColorR = 255u;
constexpr uint8_t kColorG = 127u;
constexpr uint8_t kColorB = 63u;
constexpr unsigned int kFullResolutionWidth = 128;
constexpr unsigned int kFullResolutionHeight = 96;
constexpr unsigned int kScaledDownResolutionWidth = 64;
constexpr unsigned int kScaledDownResolutionHeight = 48;
// NV12 a.k.a. 420v
constexpr OSType kPixelFormatNv12 =
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
// UYVY a.k.a. 2vuy
constexpr OSType kPixelFormatUyvy = kCVPixelFormatType_422YpCbCr8;
// YUY2 a.k.a. yuvs
constexpr OSType kPixelFormatYuy2 = kCVPixelFormatType_422YpCbCr8_yuvs;
// I420 a.k.a. y420
constexpr OSType kPixelFormatI420 = kCVPixelFormatType_420YpCbCr8Planar;
auto SupportedCaptureFormats() {
return ::testing::Values(kPixelFormatNv12, kPixelFormatUyvy,
kPixelFormatYuy2);
}
auto SupportedOutputFormats() {
return ::testing::Values(kPixelFormatNv12, kPixelFormatI420);
}
// Gives parameterized tests a readable suffix.
// E.g. ".../yuvsTo420v" instead of ".../4"
std::string TestParametersOSTypeTupleToString(
testing::TestParamInfo<std::tuple<OSType, OSType>> info) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = info.param;
return MacFourCCToString(input_pixel_format) + std::string("To") +
MacFourCCToString(output_pixel_format);
}
std::string TestParametersOSTypeToString(testing::TestParamInfo<OSType> info) {
return MacFourCCToString(info.param);
}
base::ScopedCFTypeRef<CVPixelBufferRef> CreatePixelBuffer(OSType pixel_format,
int width,
int height,
uint8_t r,
uint8_t g,
uint8_t b) {
// Create a YUVS buffer in main memory.
std::unique_ptr<ByteArrayPixelBuffer> yuvs_buffer =
CreateYuvsPixelBufferFromSingleRgbColor(width, height, r, g, b);
// Convert and/or transfer to a pixel buffer that has an IOSurface.
base::ScopedCFTypeRef<CVPixelBufferRef> pixel_buffer =
PixelBufferPool::Create(pixel_format, width, height, 1)->CreateBuffer();
PixelBufferTransferer transferer;
bool success =
transferer.TransferImage(yuvs_buffer->pixel_buffer, pixel_buffer);
DCHECK(success);
return pixel_buffer;
}
enum class PixelBufferType {
kIoSurfaceBacked,
kIoSurfaceMissing,
};
void NonPlanarCvPixelBufferReleaseCallback(void* releaseRef, const void* data) {
free(const_cast<void*>(data));
}
void PlanarCvPixelBufferReleaseCallback(void* releaseRef,
const void* data,
size_t size,
size_t num_planes,
const void* planes[]) {
free(const_cast<void*>(data));
for (size_t plane = 0; plane < num_planes; ++plane)
free(const_cast<void*>(planes[plane]));
}
std::pair<uint8_t*, size_t> GetDataAndStride(CVPixelBufferRef pixel_buffer,
size_t plane) {
if (CVPixelBufferIsPlanar(pixel_buffer)) {
return {static_cast<uint8_t*>(
CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, plane)),
CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, plane)};
} else {
DCHECK_EQ(plane, 0u) << "Non-planar pixel buffers only have 1 plane.";
return {static_cast<uint8_t*>(CVPixelBufferGetBaseAddress(pixel_buffer)),
CVPixelBufferGetBytesPerRow(pixel_buffer)};
}
}
base::ScopedCFTypeRef<CVPixelBufferRef> AddPadding(
CVPixelBufferRef pixel_buffer,
OSType pixel_format,
int width,
int height,
int padding) {
size_t num_planes = CVPixelBufferGetPlaneCount(pixel_buffer);
size_t padded_size = 0;
std::vector<size_t> plane_widths;
std::vector<size_t> plane_heights;
std::vector<size_t> plane_strides;
if (CVPixelBufferIsPlanar(pixel_buffer)) {
for (size_t plane = 0; plane < num_planes; ++plane) {
size_t plane_stride =
CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, plane);
size_t padded_stride = plane_stride + padding;
size_t h = CVPixelBufferGetHeightOfPlane(pixel_buffer, plane);
size_t w = CVPixelBufferGetWidthOfPlane(pixel_buffer, plane);
plane_heights.push_back(h);
plane_widths.push_back(w);
plane_strides.push_back(padded_stride);
padded_size += h * padded_stride;
}
} else {
// CVPixelBufferGetPlaneCount returns 0 for non-planar buffers.
num_planes = 1;
size_t plane_stride = CVPixelBufferGetBytesPerRow(pixel_buffer);
size_t padded_stride = plane_stride + padding;
size_t h = CVPixelBufferGetHeight(pixel_buffer);
padded_size += h * padded_stride;
plane_heights.push_back(h);
plane_strides.push_back(padded_stride);
}
std::vector<void*> plane_address;
CHECK_EQ(
CVPixelBufferLockBaseAddress(pixel_buffer, kCVPixelBufferLock_ReadOnly),
kCVReturnSuccess);
// Allocate and copy each plane.
for (size_t plane = 0; plane < num_planes; ++plane) {
plane_address.push_back(
calloc(1, plane_strides[plane] * plane_heights[plane]));
uint8_t* dst_ptr = static_cast<uint8_t*>(plane_address[plane]);
uint8_t* src_ptr;
size_t plane_stride;
std::tie(src_ptr, plane_stride) = GetDataAndStride(pixel_buffer, plane);
CHECK(dst_ptr);
CHECK(src_ptr);
for (size_t r = 0; r < plane_heights[plane]; ++r) {
memcpy(dst_ptr, src_ptr, plane_stride);
src_ptr += plane_stride;
dst_ptr += plane_strides[plane];
}
}
CHECK_EQ(
CVPixelBufferUnlockBaseAddress(pixel_buffer, kCVPixelBufferLock_ReadOnly),
kCVReturnSuccess);
base::ScopedCFTypeRef<CVPixelBufferRef> padded_pixel_buffer;
CVReturn create_buffer_result;
if (CVPixelBufferIsPlanar(pixel_buffer)) {
// Without some memory block the callback won't be called and we leak the
// planar data.
void* descriptor = calloc(1, sizeof(CVPlanarPixelBufferInfo_YCbCrPlanar));
create_buffer_result = CVPixelBufferCreateWithPlanarBytes(
nullptr, width, height, pixel_format, descriptor, 0, num_planes,
plane_address.data(), plane_widths.data(), plane_heights.data(),
plane_strides.data(), &PlanarCvPixelBufferReleaseCallback,
plane_strides.data(), nullptr, padded_pixel_buffer.InitializeInto());
} else {
create_buffer_result = CVPixelBufferCreateWithBytes(
nullptr, width, height, pixel_format, plane_address[0],
plane_strides[0], &NonPlanarCvPixelBufferReleaseCallback, nullptr,
nullptr, padded_pixel_buffer.InitializeInto());
}
DCHECK_EQ(create_buffer_result, kCVReturnSuccess);
return padded_pixel_buffer;
}
base::ScopedCFTypeRef<CMSampleBufferRef> CreateSampleBuffer(
OSType pixel_format,
int width,
int height,
uint8_t r,
uint8_t g,
uint8_t b,
PixelBufferType pixel_buffer_type,
size_t padding = 0) {
base::ScopedCFTypeRef<CVPixelBufferRef> pixel_buffer =
CreatePixelBuffer(pixel_format, width, height, r, g, b);
if (padding != 0) {
CHECK_EQ(pixel_buffer_type, PixelBufferType::kIoSurfaceMissing)
<< "Padding does not work with IOSurfaces.";
}
if (pixel_buffer_type == PixelBufferType::kIoSurfaceMissing) {
// Our pixel buffer currently has an IOSurface. To get rid of it, we perform
// a pixel buffer transfer to a destination pixel buffer that is not backed
// by an IOSurface. The resulting pixel buffer will have the desired color.
base::ScopedCFTypeRef<CVPixelBufferRef> iosurfaceless_pixel_buffer;
CVReturn create_buffer_result =
CVPixelBufferCreate(nullptr, width, height, pixel_format, nullptr,
iosurfaceless_pixel_buffer.InitializeInto());
DCHECK_EQ(create_buffer_result, kCVReturnSuccess);
PixelBufferTransferer transferer;
bool success =
transferer.TransferImage(pixel_buffer, iosurfaceless_pixel_buffer);
DCHECK(success);
DCHECK(!CVPixelBufferGetIOSurface(iosurfaceless_pixel_buffer));
pixel_buffer = iosurfaceless_pixel_buffer;
if (padding > 0) {
pixel_buffer =
AddPadding(pixel_buffer, pixel_format, width, height, padding);
}
}
// Wrap the pixel buffer in a sample buffer.
base::ScopedCFTypeRef<CMFormatDescriptionRef> format_description;
OSStatus status = CMVideoFormatDescriptionCreateForImageBuffer(
nil, pixel_buffer, format_description.InitializeInto());
DCHECK(status == noErr);
// Dummy information to make CMSampleBufferCreateForImageBuffer() happy.
CMSampleTimingInfo timing_info;
timing_info.decodeTimeStamp = kCMTimeInvalid;
timing_info.presentationTimeStamp = CMTimeMake(0, CMTimeScale(NSEC_PER_SEC));
timing_info.duration =
CMTimeMake(33 * NSEC_PER_MSEC, CMTimeScale(NSEC_PER_SEC)); // 30 fps
base::ScopedCFTypeRef<CMSampleBufferRef> sample_buffer;
status = CMSampleBufferCreateForImageBuffer(
nil, pixel_buffer, YES, nil, nullptr, format_description, &timing_info,
sample_buffer.InitializeInto());
DCHECK(status == noErr);
return sample_buffer;
}
base::ScopedCFTypeRef<CMSampleBufferRef> CreateMjpegSampleBuffer(
const uint8_t* mjpeg_data,
size_t mjpeg_data_size,
size_t width,
size_t height) {
CMBlockBufferCustomBlockSource source = {0};
source.FreeBlock = [](void* refcon, void* doomedMemoryBlock,
size_t sizeInBytes) {
// Do nothing. The data to be released is not dynamically allocated in this
// test code.
};
base::ScopedCFTypeRef<CMBlockBufferRef> data_buffer;
OSStatus status = CMBlockBufferCreateWithMemoryBlock(
nil, const_cast<void*>(static_cast<const void*>(mjpeg_data)),
mjpeg_data_size, nil, &source, 0, mjpeg_data_size, 0,
data_buffer.InitializeInto());
DCHECK(status == noErr);
base::ScopedCFTypeRef<CMFormatDescriptionRef> format_description;
status = CMVideoFormatDescriptionCreate(nil, kCMVideoCodecType_JPEG_OpenDML,
width, height, nil,
format_description.InitializeInto());
DCHECK(status == noErr);
// Dummy information to make CMSampleBufferCreateReady() happy.
CMSampleTimingInfo timing_info;
timing_info.decodeTimeStamp = kCMTimeInvalid;
timing_info.presentationTimeStamp = CMTimeMake(0, CMTimeScale(NSEC_PER_SEC));
timing_info.duration =
CMTimeMake(33 * NSEC_PER_MSEC, CMTimeScale(NSEC_PER_SEC)); // 30 fps
base::ScopedCFTypeRef<CMSampleBufferRef> sample_buffer;
status = CMSampleBufferCreateReady(nil, data_buffer, format_description, 1, 1,
&timing_info, 1, &kExampleJpegDataSize,
sample_buffer.InitializeInto());
DCHECK(status == noErr);
return sample_buffer;
}
base::ScopedCFTypeRef<CMSampleBufferRef> CreateExampleMjpegSampleBuffer() {
// Sanity-check the example data.
int width;
int height;
int result =
libyuv::MJPGSize(kExampleJpegData, kExampleJpegDataSize, &width, &height);
DCHECK(result == 0);
DCHECK_EQ(width, static_cast<int>(kExampleJpegWidth));
DCHECK_EQ(height, static_cast<int>(kExampleJpegHeight));
return CreateMjpegSampleBuffer(kExampleJpegData, kExampleJpegDataSize,
kExampleJpegWidth, kExampleJpegHeight);
}
base::ScopedCFTypeRef<CMSampleBufferRef> CreateInvalidMjpegSampleBuffer() {
return CreateMjpegSampleBuffer(kInvalidJpegData, kInvalidJpegDataSize,
kExampleJpegWidth, kExampleJpegHeight);
}
} // namespace
class SampleBufferTransformerPixelTransferTest
: public ::testing::Test,
public ::testing::WithParamInterface<std::tuple<OSType, OSType>> {};
TEST_P(SampleBufferTransformerPixelTransferTest, CanConvertFullScale) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceBacked);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kPixelBufferTransfer,
output_pixel_format,
gfx::Size(kFullResolutionWidth, kFullResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kFullResolutionWidth, CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kFullResolutionHeight, CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
#if defined(ARCH_CPU_ARM64)
// Bulk-disabled for arm64 bot stabilization: https://crbug.com/1154345
#define MAYBE_CanConvertAndScaleDown DISABLED_CanConvertAndScaleDown
#else
#define MAYBE_CanConvertAndScaleDown CanConvertAndScaleDown
#endif
TEST_P(SampleBufferTransformerPixelTransferTest, MAYBE_CanConvertAndScaleDown) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceBacked);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kPixelBufferTransfer,
output_pixel_format,
gfx::Size(kScaledDownResolutionWidth, kScaledDownResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionWidth,
CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionHeight,
CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
TEST_P(SampleBufferTransformerPixelTransferTest,
CanConvertAndScaleDownWhenIoSurfaceIsMissing) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceMissing);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kPixelBufferTransfer,
output_pixel_format,
gfx::Size(kScaledDownResolutionWidth, kScaledDownResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionWidth,
CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionHeight,
CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
TEST_P(SampleBufferTransformerPixelTransferTest,
CanConvertWithPaddingFullScale) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceMissing, /*padding*/ 100);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kPixelBufferTransfer,
output_pixel_format,
gfx::Size(kFullResolutionWidth, kFullResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kFullResolutionWidth, CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kFullResolutionHeight, CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
TEST_P(SampleBufferTransformerPixelTransferTest,
CanConvertAndScaleWithPadding) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceMissing, /*padding*/ 100);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kPixelBufferTransfer,
output_pixel_format,
gfx::Size(kScaledDownResolutionWidth, kScaledDownResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionWidth,
CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionHeight,
CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
INSTANTIATE_TEST_SUITE_P(SampleBufferTransformerTest,
SampleBufferTransformerPixelTransferTest,
::testing::Combine(SupportedCaptureFormats(),
SupportedOutputFormats()),
TestParametersOSTypeTupleToString);
class SampleBufferTransformerLibyuvTest
: public ::testing::Test,
public ::testing::WithParamInterface<std::tuple<OSType, OSType>> {};
TEST_P(SampleBufferTransformerLibyuvTest, CanConvertFullScale) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceBacked);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kLibyuv, output_pixel_format,
gfx::Size(kFullResolutionWidth, kFullResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kFullResolutionWidth, CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kFullResolutionHeight, CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
TEST_P(SampleBufferTransformerLibyuvTest, MAYBE_CanConvertAndScaleDown) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceBacked);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kLibyuv, output_pixel_format,
gfx::Size(kScaledDownResolutionWidth, kScaledDownResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionWidth,
CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionHeight,
CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
TEST_P(SampleBufferTransformerLibyuvTest, CanConvertWithPaddingFullScale) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceMissing, /*padding*/ 100);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kLibyuv, output_pixel_format,
gfx::Size(kFullResolutionWidth, kFullResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kFullResolutionWidth, CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kFullResolutionHeight, CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
TEST_P(SampleBufferTransformerLibyuvTest, CanConvertAndScaleWithPadding) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceMissing, /*padding*/ 100);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kLibyuv, output_pixel_format,
gfx::Size(kScaledDownResolutionWidth, kScaledDownResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionWidth,
CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionHeight,
CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
TEST_P(SampleBufferTransformerLibyuvTest,
CanConvertAndScaleDownWhenIoSurfaceIsMissing) {
OSType input_pixel_format;
OSType output_pixel_format;
std::tie(input_pixel_format, output_pixel_format) = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateSampleBuffer(input_pixel_format, kFullResolutionWidth,
kFullResolutionHeight, kColorR, kColorG, kColorB,
PixelBufferType::kIoSurfaceMissing);
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kLibyuv, output_pixel_format,
gfx::Size(kScaledDownResolutionWidth, kScaledDownResolutionHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionWidth,
CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kScaledDownResolutionHeight,
CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
INSTANTIATE_TEST_SUITE_P(SampleBufferTransformerTest,
SampleBufferTransformerLibyuvTest,
::testing::Combine(SupportedCaptureFormats(),
SupportedOutputFormats()),
TestParametersOSTypeTupleToString);
class SampleBufferTransformerMjpegTest
: public ::testing::Test,
public ::testing::WithParamInterface<OSType> {};
TEST_P(SampleBufferTransformerMjpegTest, CanConvertFullScale) {
OSType output_pixel_format = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateExampleMjpegSampleBuffer();
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(SampleBufferTransformer::Transformer::kLibyuv,
output_pixel_format,
gfx::Size(kExampleJpegWidth, kExampleJpegHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_EQ(kExampleJpegWidth, CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kExampleJpegHeight, CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
TEST_P(SampleBufferTransformerMjpegTest, MAYBE_CanConvertAndScaleDown) {
OSType output_pixel_format = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateExampleMjpegSampleBuffer();
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(
SampleBufferTransformer::Transformer::kLibyuv, output_pixel_format,
gfx::Size(kExampleJpegScaledDownWidth, kExampleJpegScaledDownHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_EQ(kExampleJpegScaledDownWidth,
CVPixelBufferGetWidth(output_pixel_buffer));
EXPECT_EQ(kExampleJpegScaledDownHeight,
CVPixelBufferGetHeight(output_pixel_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
}
TEST_P(SampleBufferTransformerMjpegTest,
AttemptingToTransformInvalidMjpegFailsGracefully) {
OSType output_pixel_format = GetParam();
base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
CreateInvalidMjpegSampleBuffer();
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
transformer->Reconfigure(SampleBufferTransformer::Transformer::kLibyuv,
output_pixel_format,
gfx::Size(kExampleJpegWidth, kExampleJpegHeight), 1);
base::ScopedCFTypeRef<CVPixelBufferRef> output_pixel_buffer =
transformer->Transform(input_sample_buffer);
EXPECT_FALSE(output_pixel_buffer);
}
INSTANTIATE_TEST_SUITE_P(SampleBufferTransformerTest,
SampleBufferTransformerMjpegTest,
SupportedOutputFormats(),
TestParametersOSTypeToString);
TEST(SampleBufferTransformerBestTransformerForNv12OutputTest,
SourceAndDestinationResolutionMatches_InputSampleBuffer) {
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
base::ScopedCFTypeRef<CMSampleBufferRef> sample0 = CreateSampleBuffer(
kPixelFormatNv12, kFullResolutionWidth, kFullResolutionHeight, kColorR,
kColorG, kColorB, PixelBufferType::kIoSurfaceMissing);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample0),
kPixelFormatNv12, media::GetSampleBufferSize(sample0));
base::ScopedCFTypeRef<CVPixelBufferRef> output_buffer =
transformer->Transform(sample0);
EXPECT_EQ(gfx::Size(kFullResolutionWidth, kFullResolutionHeight),
transformer->destination_size());
EXPECT_EQ(kFullResolutionWidth, CVPixelBufferGetWidth(output_buffer));
EXPECT_EQ(kFullResolutionHeight, CVPixelBufferGetHeight(output_buffer));
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
// Because sample0 has no underlying IOSurface, it should not be returned from
// the transformer.
EXPECT_NE(output_buffer.get(), CMSampleBufferGetImageBuffer(sample0.get()));
base::ScopedCFTypeRef<CMSampleBufferRef> sample1 = CreateSampleBuffer(
kPixelFormatNv12, kScaledDownResolutionWidth, kScaledDownResolutionHeight,
kColorR, kColorG, kColorB, PixelBufferType::kIoSurfaceBacked);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample1),
kPixelFormatNv12, media::GetSampleBufferSize(sample1));
output_buffer = transformer->Transform(sample1);
EXPECT_EQ(gfx::Size(kScaledDownResolutionWidth, kScaledDownResolutionHeight),
transformer->destination_size());
EXPECT_EQ(kScaledDownResolutionWidth, CVPixelBufferGetWidth(output_buffer));
EXPECT_EQ(kScaledDownResolutionHeight, CVPixelBufferGetHeight(output_buffer));
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
// Because sample1 does have an IOSurface, it can be returned directly.
EXPECT_EQ(output_buffer.get(), CMSampleBufferGetImageBuffer(sample1.get()));
}
// Same test as above, verifying that Transform() methods work on pixel buffers
// directly (so that there's no need to have a sample buffer).
TEST(SampleBufferTransformerBestTransformerForNv12OutputTest,
SourceAndDestinationResolutionMatches_InputPixelBuffer) {
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
base::ScopedCFTypeRef<CMSampleBufferRef> sample0 = CreateSampleBuffer(
kPixelFormatNv12, kFullResolutionWidth, kFullResolutionHeight, kColorR,
kColorG, kColorB, PixelBufferType::kIoSurfaceMissing);
CVPixelBufferRef pixel0 = CMSampleBufferGetImageBuffer(sample0);
ASSERT_TRUE(pixel0);
transformer->Reconfigure(
SampleBufferTransformer::kBestTransformerForPixelBufferToNv12Output,
kPixelFormatNv12, media::GetPixelBufferSize(pixel0));
base::ScopedCFTypeRef<CVPixelBufferRef> output_buffer =
transformer->Transform(pixel0);
EXPECT_EQ(gfx::Size(kFullResolutionWidth, kFullResolutionHeight),
transformer->destination_size());
EXPECT_EQ(kFullResolutionWidth, CVPixelBufferGetWidth(output_buffer));
EXPECT_EQ(kFullResolutionHeight, CVPixelBufferGetHeight(output_buffer));
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
// Because pixel0 has no underlying IOSurface, it should not be returned from
// the transformer.
EXPECT_NE(output_buffer.get(), pixel0);
base::ScopedCFTypeRef<CMSampleBufferRef> sample1 = CreateSampleBuffer(
kPixelFormatNv12, kScaledDownResolutionWidth, kScaledDownResolutionHeight,
kColorR, kColorG, kColorB, PixelBufferType::kIoSurfaceBacked);
CVPixelBufferRef pixel1 = CMSampleBufferGetImageBuffer(sample1);
ASSERT_TRUE(pixel1);
transformer->Reconfigure(
SampleBufferTransformer::kBestTransformerForPixelBufferToNv12Output,
kPixelFormatNv12, media::GetPixelBufferSize(pixel1));
output_buffer = transformer->Transform(pixel1);
EXPECT_EQ(gfx::Size(kScaledDownResolutionWidth, kScaledDownResolutionHeight),
transformer->destination_size());
EXPECT_EQ(kScaledDownResolutionWidth, CVPixelBufferGetWidth(output_buffer));
EXPECT_EQ(kScaledDownResolutionHeight, CVPixelBufferGetHeight(output_buffer));
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
// Because pixel1 does have an IOSurface, it can be returned directly.
EXPECT_EQ(output_buffer.get(), pixel1);
}
TEST(SampleBufferTransformerBestTransformerForNv12OutputTest,
CanConvertAndScaleDown_InputPixelBuffer) {
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
base::ScopedCFTypeRef<CMSampleBufferRef> sample_buffer = CreateSampleBuffer(
kPixelFormatNv12, kFullResolutionWidth, kFullResolutionHeight, kColorR,
kColorG, kColorB, PixelBufferType::kIoSurfaceBacked);
CVPixelBufferRef pixel_buffer = CMSampleBufferGetImageBuffer(sample_buffer);
ASSERT_TRUE(pixel_buffer);
transformer->Reconfigure(
SampleBufferTransformer::kBestTransformerForPixelBufferToNv12Output,
kPixelFormatNv12,
gfx::Size(kScaledDownResolutionWidth, kScaledDownResolutionHeight));
base::ScopedCFTypeRef<CVPixelBufferRef> output_buffer =
transformer->Transform(pixel_buffer);
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
EXPECT_EQ(kScaledDownResolutionWidth, CVPixelBufferGetWidth(output_buffer));
EXPECT_EQ(kScaledDownResolutionHeight, CVPixelBufferGetHeight(output_buffer));
EXPECT_TRUE(
PixelBufferIsSingleColor(output_buffer, kColorR, kColorG, kColorB));
}
TEST(SampleBufferTransformerBestTransformerForNv12OutputTest,
DestinationPixelFormatIsAlwaysNv12) {
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
base::ScopedCFTypeRef<CMSampleBufferRef> sample = CreateSampleBuffer(
kPixelFormatNv12, kScaledDownResolutionWidth, kScaledDownResolutionHeight,
kColorR, kColorG, kColorB, PixelBufferType::kIoSurfaceBacked);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
base::ScopedCFTypeRef<CVPixelBufferRef> output_buffer =
transformer->Transform(sample);
EXPECT_EQ(kPixelFormatNv12, transformer->destination_pixel_format());
EXPECT_EQ(kPixelFormatNv12,
IOSurfaceGetPixelFormat(CVPixelBufferGetIOSurface(output_buffer)));
sample = CreateSampleBuffer(kPixelFormatUyvy, kScaledDownResolutionWidth,
kScaledDownResolutionHeight, kColorR, kColorG,
kColorB, PixelBufferType::kIoSurfaceBacked);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
output_buffer = transformer->Transform(sample);
EXPECT_EQ(kPixelFormatNv12, transformer->destination_pixel_format());
EXPECT_EQ(kPixelFormatNv12,
IOSurfaceGetPixelFormat(CVPixelBufferGetIOSurface(output_buffer)));
sample = CreateSampleBuffer(kPixelFormatYuy2, kScaledDownResolutionWidth,
kScaledDownResolutionHeight, kColorR, kColorG,
kColorB, PixelBufferType::kIoSurfaceBacked);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
output_buffer = transformer->Transform(sample);
EXPECT_EQ(kPixelFormatNv12, transformer->destination_pixel_format());
EXPECT_EQ(kPixelFormatNv12,
IOSurfaceGetPixelFormat(CVPixelBufferGetIOSurface(output_buffer)));
sample = CreateSampleBuffer(kPixelFormatI420, kScaledDownResolutionWidth,
kScaledDownResolutionHeight, kColorR, kColorG,
kColorB, PixelBufferType::kIoSurfaceBacked);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
output_buffer = transformer->Transform(sample);
EXPECT_EQ(kPixelFormatNv12, transformer->destination_pixel_format());
EXPECT_EQ(kPixelFormatNv12,
IOSurfaceGetPixelFormat(CVPixelBufferGetIOSurface(output_buffer)));
sample = CreateExampleMjpegSampleBuffer();
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
output_buffer = transformer->Transform(sample);
EXPECT_EQ(kPixelFormatNv12, transformer->destination_pixel_format());
EXPECT_EQ(kPixelFormatNv12,
IOSurfaceGetPixelFormat(CVPixelBufferGetIOSurface(output_buffer)));
}
TEST(SampleBufferTransformerBestTransformerForNv12OutputTest,
UsesBestTransformerPaths) {
std::unique_ptr<SampleBufferTransformer> transformer =
SampleBufferTransformer::Create();
base::ScopedCFTypeRef<CMSampleBufferRef> sample = CreateSampleBuffer(
kPixelFormatNv12, kScaledDownResolutionWidth, kScaledDownResolutionHeight,
kColorR, kColorG, kColorB, PixelBufferType::kIoSurfaceBacked);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
base::ScopedCFTypeRef<CVPixelBufferRef> output_buffer =
transformer->Transform(sample);
EXPECT_EQ(SampleBufferTransformer::Transformer::kPixelBufferTransfer,
transformer->transformer());
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
sample = CreateSampleBuffer(kPixelFormatUyvy, kScaledDownResolutionWidth,
kScaledDownResolutionHeight, kColorR, kColorG,
kColorB, PixelBufferType::kIoSurfaceBacked);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
output_buffer = transformer->Transform(sample);
EXPECT_EQ(SampleBufferTransformer::Transformer::kPixelBufferTransfer,
transformer->transformer());
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
sample = CreateSampleBuffer(kPixelFormatYuy2, kScaledDownResolutionWidth,
kScaledDownResolutionHeight, kColorR, kColorG,
kColorB, PixelBufferType::kIoSurfaceBacked);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
output_buffer = transformer->Transform(sample);
EXPECT_EQ(SampleBufferTransformer::Transformer::kPixelBufferTransfer,
transformer->transformer());
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
sample = CreateSampleBuffer(kPixelFormatI420, kScaledDownResolutionWidth,
kScaledDownResolutionHeight, kColorR, kColorG,
kColorB, PixelBufferType::kIoSurfaceBacked);
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
output_buffer = transformer->Transform(sample);
EXPECT_EQ(SampleBufferTransformer::Transformer::kPixelBufferTransfer,
transformer->transformer());
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
sample = CreateExampleMjpegSampleBuffer();
transformer->Reconfigure(
SampleBufferTransformer::GetBestTransformerForNv12Output(sample),
kPixelFormatNv12, media::GetSampleBufferSize(sample));
output_buffer = transformer->Transform(sample);
EXPECT_EQ(SampleBufferTransformer::Transformer::kLibyuv,
transformer->transformer());
EXPECT_TRUE(CVPixelBufferGetIOSurface(output_buffer));
}
} // namespace media