FreeRDP
Loading...
Searching...
No Matches
TestCameraAVFoundationModes.m
1
18#include "../modes.h"
19
20/* These stand-ins provide the read-only format/range selectors without opening
21 * a camera. Native AVCaptureDeviceFormat objects have no public initializer. */
22@interface TestAVFoundationRange : NSObject
23@property(nonatomic) CMTime minFrameDuration;
24@property(nonatomic) CMTime maxFrameDuration;
25@end
26@implementation TestAVFoundationRange
27@end
28
29static unsigned liveFormats = 0;
30
31@interface TestAVFoundationFormat : NSObject
32@property(nonatomic) CMVideoFormatDescriptionRef formatDescription;
33@property(nonatomic, copy) NSArray *videoSupportedFrameRateRanges;
34@end
35@implementation TestAVFoundationFormat
36- (instancetype)init
37{
38 self = [super init];
39 if (self)
40 liveFormats++;
41 return self;
42}
43- (void)dealloc
44{
45 liveFormats--;
46 if (_formatDescription)
47 CFRelease(_formatDescription);
48 [_videoSupportedFrameRateRanges release];
49 [super dealloc];
50}
51@end
52
53static TestAVFoundationRange *test_range(CMTime duration)
54{
55 TestAVFoundationRange *range = [[[TestAVFoundationRange alloc] init] autorelease];
56 range.minFrameDuration = duration;
57 range.maxFrameDuration = duration;
58 return range;
59}
60
61static AVCaptureDeviceFormat *test_format(int width, int height, NSArray *ranges)
62{
63 TestAVFoundationFormat *format = [[[TestAVFoundationFormat alloc] init] autorelease];
64 CMVideoFormatDescriptionRef description = NULL;
65 if (CMVideoFormatDescriptionCreate(NULL, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, width,
66 height, NULL, &description) != noErr)
67 return nil;
68 format.formatDescription = description;
69 format.videoSupportedFrameRateRanges = ranges;
70 return (AVCaptureDeviceFormat *)format;
71}
72
73static BOOL test_modes(void)
74{
75 BOOL success = FALSE;
77 AVCaptureDeviceFormat *original = nil;
78 const CMTime native30 = CMTimeMake(1000000, 30000030);
79 const CMTime native15 = CMTimeMake(1000000, 15000015);
80 const CMTime unusual30 = CMTimeMake(33333333, 1000000000);
81 const CMTime nearby30 = CMTimeMake(1000000, 30000060);
82 CAM_MEDIA_TYPE_DESCRIPTION types[8] = { 0 };
83 size_t count = 0;
84 @autoreleasepool
85 {
86 NSArray *rates = @[test_range(native30), test_range(native15)];
87 original = test_format(640, 480, rates);
88 AVCaptureDeviceFormat *duplicate = test_format(640, 480, rates);
89 AVCaptureDeviceFormat *unusual = test_format(854, 480, @[test_range(unusual30)]);
90 AVCaptureDeviceFormat *nearby = test_format(640, 480, @[test_range(nearby30)]);
91 AVCaptureDeviceFormat *odd = test_format(641, 480, rates);
92 AVCaptureDeviceFormat *oversized = test_format(3840, 2160, rates);
93 if (!original || !duplicate || !unusual || !nearby || !odd || !oversized)
94 return FALSE;
95 NSMutableArray *formats =
96 [NSMutableArray arrayWithArray:@[original, duplicate, unusual, nearby, odd, oversized]];
97 modes = [[FreeRDPCameraAVFoundationModes alloc] initWithFormats:formats];
98 if (!modes)
99 return FALSE;
100 count = [modes copyMediaTypes:types capacity:ARRAYSIZE(types)];
101 /* Reordering/removing the source formats must not change an advertised selection. */
102 [formats removeObjectAtIndex:0];
103 [(TestAVFoundationFormat *)original setVideoSupportedFrameRateRanges:@[]];
104 }
105 /* Only formats referenced by advertised entries should survive the source pool. */
106 if ((count != 4) || (liveFormats != 3) || (types[0].Width != 640) || (types[0].Height != 480) ||
107 (types[0].FrameRateNumerator != 3000003) || (types[0].FrameRateDenominator != 100000) ||
108 (types[1].FrameRateDenominator != 200000) || (types[2].Width != 854) ||
109 (types[2].FrameRateNumerator != 1000000000) ||
110 (types[2].FrameRateDenominator != 33333333) || (types[3].FrameRateNumerator != 1500003) ||
111 (types[3].FrameRateDenominator != 50000))
112 {
113 fprintf(stderr, "Native mode enumeration, deduplication or retention failed\n");
114 goto out;
115 }
116 const CMTime durations[] = { native30, native15, unusual30, nearby30 };
117 for (size_t i = 0; i < count; i++)
118 {
119 CMTime actual = kCMTimeInvalid;
120 AVCaptureDeviceFormat *format = [modes formatForMediaType:&types[i] frameDuration:&actual];
121 if (!format || ((i < 2) && (format != original)) || (actual.value != durations[i].value) ||
122 (actual.timescale != durations[i].timescale) || (actual.flags != durations[i].flags) ||
123 (actual.epoch != durations[i].epoch))
124 {
125 fprintf(stderr, "Cached native format/duration changed for mode %zu\n", i);
126 goto out;
127 }
128 }
129 /* Repeated enumeration and a capacity-limited copy preserve the snapshot. */
130 CAM_MEDIA_TYPE_DESCRIPTION again[8] = { 0 };
131 if (([modes copyMediaTypes:again capacity:ARRAYSIZE(again)] != count) ||
132 (memcmp(types, again, count * sizeof(types[0])) != 0) ||
133 ([modes copyMediaTypes:again capacity:0] != 0) ||
134 ([modes copyMediaTypes:again capacity:1] != 1) ||
135 (memcmp(types, again, sizeof(types[0])) != 0))
136 {
137 fprintf(stderr, "Advertised mode snapshot changed\n");
138 goto out;
139 }
140 CAM_MEDIA_TYPE_DESCRIPTION request = types[0];
141 CMTime duration = kCMTimeInvalid;
142 request.Flags = CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired;
143 if ([modes formatForMediaType:&request frameDuration:&duration] != original)
144 {
145 fprintf(stderr, "Shared encoder flags changed native mode lookup\n");
146 goto out;
147 }
148 /* An unadvertised near-match must be rejected, never searched or approximated. */
149 for (size_t i = 0; i < 5; i++)
150 {
151 request = types[0];
152 switch (i)
153 {
154 case 0:
155 request.FrameRateNumerator = 30;
156 request.FrameRateDenominator = 1;
157 break;
158 case 1:
159 request.Width += 2;
160 break;
161 case 2:
162 request.Height += 2;
163 break;
164 case 3:
165 request.PixelAspectRatioNumerator = 2;
166 break;
167 case 4:
168 request.Format = CAM_MEDIA_FORMAT_H264;
169 break;
170 }
171 if ([modes formatForMediaType:&request frameDuration:&duration] ||
172 CMTIME_IS_VALID(duration))
173 {
174 fprintf(stderr, "Unadvertised mode accepted in case %zu\n", i);
175 goto out;
176 }
177 }
178 success = TRUE;
179out:
180 [modes release];
181 if (liveFormats != 0)
182 {
183 fprintf(stderr, "Cached native formats were not released\n");
184 success = FALSE;
185 }
186 return success;
187}
188
189int main(int argc, char *argv[])
190{
191 WINPR_UNUSED(argc);
192 WINPR_UNUSED(argv);
193 @autoreleasepool
194 {
195 return test_modes() ? 0 : -1;
196 }
197}