22#include <VideoToolbox/VideoToolbox.h>
26static BOOL test_scaling(
size_t width,
size_t height,
size_t outWidth,
size_t outHeight,
30 CVPixelBufferRef source = NULL;
31 CVPixelBufferRef destination = NULL;
32 CFTypeRef transfer = NULL;
33 wStream* packed = Stream_New(NULL, 1);
34 const OSType format = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
36 (CVPixelBufferCreate(NULL, width, height, format, NULL, &source) != kCVReturnSuccess) ||
37 (CVPixelBufferCreate(NULL, outWidth, outHeight, format, NULL, &destination) !=
40 CFNumberRef horizontal = CFNumberCreate(NULL, kCFNumberIntType, &pixelAspect);
42 CFNumberRef vertical = CFNumberCreate(NULL, kCFNumberIntType, &one);
43 const void* keys[] = { kCVImageBufferPixelAspectRatioHorizontalSpacingKey,
44 kCVImageBufferPixelAspectRatioVerticalSpacingKey };
45 const void* values[] = { horizontal, vertical };
46 CFDictionaryRef aspect = CFDictionaryCreate(
47 NULL, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
48 CVBufferSetAttachment(source, kCVImageBufferPixelAspectRatioKey, aspect,
49 kCVAttachmentMode_ShouldPropagate);
51 CFRelease(horizontal);
53 if (CVPixelBufferLockBaseAddress(source, 0) != kCVReturnSuccess)
55 BYTE* luma = CVPixelBufferGetBaseAddressOfPlane(source, 0);
56 const size_t stride = CVPixelBufferGetBytesPerRowOfPlane(source, 0);
57 for (
size_t y = 0; y < height; y++)
59 memset(luma + y * stride, 64, width);
60 for (
size_t x = 0; x < width; x++)
62 const double dx = ((double)x + 0.5 - (
double)width / 2) * pixelAspect;
63 const double dy = (double)y + 0.5 - (
double)height / 2;
65 if (dx * dx + dy * dy < 24 * 24)
66 luma[y * stride + x] = 224;
68 if ((y >= height / 6 - 12) && (y < height / 6 + 12))
70 if ((x >= width / 12) && (x < width / 12 + width / 4))
71 luma[y * stride + x] = 112;
72 if ((x >= width - width / 12 - width / 4) && (x < width - width / 12))
73 luma[y * stride + x] = 176;
77 BYTE* chroma = CVPixelBufferGetBaseAddressOfPlane(source, 1);
78 const size_t chromaStride = CVPixelBufferGetBytesPerRowOfPlane(source, 1);
79 for (
size_t y = 0; y < height / 2; y++)
80 memset(chroma + y * chromaStride, 128, width);
81 CVPixelBufferUnlockBaseAddress(source, 0);
82 if ((cam_avfoundation_pixel_transfer_create(&transfer) != noErr) ||
83 (VTPixelTransferSessionTransferImage((VTPixelTransferSessionRef)transfer, source,
84 destination) != noErr) ||
85 !cam_avfoundation_pixel_buffer_to_nv12(destination, packed) ||
86 (Stream_Length(packed) != outWidth * outHeight * 3 / 2))
88 const BYTE* data = Stream_Buffer(packed);
89 size_t minX = outWidth;
90 size_t minY = outHeight;
93 for (
size_t y = 0; y < outHeight; y++)
95 for (
size_t x = 0; x < outWidth; x++)
97 if (data[y * outWidth + x] <= 200)
109 if ((minX >= outWidth) || (minY >= outHeight) ||
110 (abs((
int)(maxX - minX) - (
int)(maxY - minY)) > 2))
112 fprintf(stderr,
"Circle distorted: %zux%zu -> %zux%zu, PAR %d:1, bounds %zu,%zu-%zu,%zu\n",
113 width, height, outWidth, outHeight, pixelAspect, minX, minY, maxX, maxY);
117 fmin((
double)outWidth / ((
double)width * pixelAspect), (
double)outHeight / height);
118 const double paddingX = ((double)outWidth - width * pixelAspect * scale) / 2;
119 const double paddingY = ((double)outHeight - height * scale) / 2;
120 const size_t markerY = (size_t)(paddingY + (height / 6) * scale);
121 const size_t left = (size_t)(paddingX + (width / 12 + width / 8) * pixelAspect * scale);
123 (size_t)(paddingX + (width - width / 12 - width / 8) * pixelAspect * scale);
124 const int leftValue = data[markerY * outWidth + left];
125 const int rightValue = data[markerY * outWidth + right];
126 if ((abs(leftValue - 112) > 16) || (abs(rightValue - 176) > 16))
130 "Corner markers moved or flipped: %zux%zu -> %zux%zu, PAR %d:1, left=%d, right=%d\n",
131 width, height, outWidth, outHeight, pixelAspect, leftValue, rightValue);
134 if (((paddingX > 1) || (paddingY > 1)) && (data[0] > 24))
136 fprintf(stderr,
"Letterbox padding is not black\n");
146 CFRelease(destination);
147 Stream_Free(packed, TRUE);
151int TestCameraAVFoundationScaling(
int argc,
char* argv[])
155 return (test_scaling(192, 108, 64, 48, 1) && test_scaling(108, 192, 64, 48, 1) &&
156 test_scaling(192, 108, 64, 36, 1) && test_scaling(192, 108, 64, 48, 2))