FreeRDP
Loading...
Searching...
No Matches
TestCameraAVFoundationScaling.c
1
18#include <math.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <VideoToolbox/VideoToolbox.h>
23#include "../nv12.h"
24
25WINPR_ATTR_NODISCARD
26static BOOL test_scaling(size_t width, size_t height, size_t outWidth, size_t outHeight,
27 int pixelAspect)
28{
29 BOOL result = FALSE;
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;
35 if (!packed ||
36 (CVPixelBufferCreate(NULL, width, height, format, NULL, &source) != kCVReturnSuccess) ||
37 (CVPixelBufferCreate(NULL, outWidth, outHeight, format, NULL, &destination) !=
38 kCVReturnSuccess))
39 goto out;
40 CFNumberRef horizontal = CFNumberCreate(NULL, kCFNumberIntType, &pixelAspect);
41 const int one = 1;
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);
50 CFRelease(aspect);
51 CFRelease(horizontal);
52 CFRelease(vertical);
53 if (CVPixelBufferLockBaseAddress(source, 0) != kCVReturnSuccess)
54 goto out;
55 BYTE* luma = CVPixelBufferGetBaseAddressOfPlane(source, 0);
56 const size_t stride = CVPixelBufferGetBytesPerRowOfPlane(source, 0);
57 for (size_t y = 0; y < height; y++)
58 {
59 memset(luma + y * stride, 64, width);
60 for (size_t x = 0; x < width; x++)
61 {
62 const double dx = ((double)x + 0.5 - (double)width / 2) * pixelAspect;
63 const double dy = (double)y + 0.5 - (double)height / 2;
64 /* A physical circle must remain circular after scaling. */
65 if (dx * dx + dy * dy < 24 * 24)
66 luma[y * stride + x] = 224;
67 /* Distinct corner markers detect any horizontal or vertical flip. */
68 if ((y >= height / 6 - 12) && (y < height / 6 + 12))
69 {
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;
74 }
75 }
76 }
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))
87 goto out;
88 const BYTE* data = Stream_Buffer(packed);
89 size_t minX = outWidth;
90 size_t minY = outHeight;
91 size_t maxX = 0;
92 size_t maxY = 0;
93 for (size_t y = 0; y < outHeight; y++)
94 {
95 for (size_t x = 0; x < outWidth; x++)
96 {
97 if (data[y * outWidth + x] <= 200)
98 continue;
99 if (x < minX)
100 minX = x;
101 if (x > maxX)
102 maxX = x;
103 if (y < minY)
104 minY = y;
105 if (y > maxY)
106 maxY = y;
107 }
108 }
109 if ((minX >= outWidth) || (minY >= outHeight) ||
110 (abs((int)(maxX - minX) - (int)(maxY - minY)) > 2))
111 {
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);
114 goto out;
115 }
116 const double scale =
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);
122 const size_t right =
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))
127 {
128 fprintf(
129 stderr,
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);
132 goto out;
133 }
134 if (((paddingX > 1) || (paddingY > 1)) && (data[0] > 24))
135 {
136 fprintf(stderr, "Letterbox padding is not black\n");
137 goto out;
138 }
139 result = TRUE;
140out:
141 if (transfer)
142 CFRelease(transfer);
143 if (source)
144 CFRelease(source);
145 if (destination)
146 CFRelease(destination);
147 Stream_Free(packed, TRUE);
148 return result;
149}
150
151int TestCameraAVFoundationScaling(int argc, char* argv[])
152{
153 WINPR_UNUSED(argc);
154 WINPR_UNUSED(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))
157 ? 0
158 : -1;
159}