@expo/serve-sim 0.1.35-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +279 -0
  3. package/Sources/SimAXSettings/build.sh +21 -0
  4. package/Sources/SimAXSettings/sim-ax-settings.m +273 -0
  5. package/Sources/SimCameraHelper/build.sh +33 -0
  6. package/Sources/SimCameraHelper/main.m +955 -0
  7. package/Sources/SimCameraInjector/SimCamFakes.h +88 -0
  8. package/Sources/SimCameraInjector/SimCamFakes.m +704 -0
  9. package/Sources/SimCameraInjector/SimCamFrameSource.h +26 -0
  10. package/Sources/SimCameraInjector/SimCamFrameSource.m +577 -0
  11. package/Sources/SimCameraInjector/SimCamLog.h +5 -0
  12. package/Sources/SimCameraInjector/SimCamLog.m +9 -0
  13. package/Sources/SimCameraInjector/SimCamSwizzles.h +3 -0
  14. package/Sources/SimCameraInjector/SimCamSwizzles.m +1338 -0
  15. package/Sources/SimCameraInjector/SimCameraInjector.m +19 -0
  16. package/Sources/SimCameraInjector/build.sh +39 -0
  17. package/Sources/SimCameraInjector/include/SimCamShared.h +79 -0
  18. package/dist/bin/LiveKitWebRTC.framework/LiveKitWebRTC +0 -0
  19. package/dist/bin/LiveKitWebRTC.framework/Resources/Info.plist +36 -0
  20. package/dist/bin/LiveKitWebRTC.framework/Resources/LICENSE.webrtc +29 -0
  21. package/dist/bin/LiveKitWebRTC.framework/Resources/PrivacyInfo.xcprivacy +32 -0
  22. package/dist/bin/LiveKitWebRTC.framework/_CodeSignature/CodeResources +150 -0
  23. package/dist/middleware.cjs +2 -0
  24. package/dist/middleware.js +123 -0
  25. package/dist/native/serve-sim-native.node +0 -0
  26. package/dist/serve-sim.js +218 -0
  27. package/dist/simax/serve-sim-ax-settings +0 -0
  28. package/dist/simcam/libSimCameraInjector.dylib +0 -0
  29. package/dist/simcam/serve-sim-camera-helper +0 -0
  30. package/dist/state.js +1 -0
  31. package/package.json +99 -0
  32. package/src/ax-shared.ts +25 -0
  33. package/src/ax.ts +258 -0
  34. package/src/camera-helper.ts +150 -0
  35. package/src/connect-to-fetch.ts +239 -0
  36. package/src/middleware.ts +2208 -0
  37. package/src/native.ts +294 -0
  38. package/src/state.ts +86 -0
  39. package/src/stream-settings.ts +202 -0
@@ -0,0 +1,704 @@
1
+ #import "SimCamFakes.h"
2
+ #import "SimCamLog.h"
3
+
4
+ #import <CoreImage/CoreImage.h>
5
+ #import <CoreMedia/CoreMedia.h>
6
+ #import <CoreVideo/CoreVideo.h>
7
+ #import <UIKit/UIKit.h>
8
+ #import <objc/runtime.h>
9
+ #import <objc/message.h>
10
+ #include <stdatomic.h>
11
+ #include <string.h>
12
+
13
+ #pragma mark - Mirror mode
14
+
15
+ static SimCamMirrorMode gMirrorMode = SimCamMirrorAuto;
16
+
17
+ SimCamMirrorMode SimCamGetMirrorMode(void) { return gMirrorMode; }
18
+ void SimCamSetMirrorMode(SimCamMirrorMode m) { gMirrorMode = m; }
19
+
20
+ BOOL SimCamShouldMirror(AVCaptureDevicePosition p) {
21
+ if (gMirrorMode == SimCamMirrorForceOn) return YES;
22
+ if (gMirrorMode == SimCamMirrorForceOff) return NO;
23
+ return p == AVCaptureDevicePositionFront;
24
+ }
25
+
26
+ void SimCamReadMirrorModeFromEnv(void) {
27
+ const char *m = getenv("SIMCAM_MIRROR_MODE");
28
+ if (!m) return;
29
+ if (!strcasecmp(m, "on") || !strcmp(m, "1") || !strcasecmp(m, "true")) {
30
+ gMirrorMode = SimCamMirrorForceOn;
31
+ simcam_log(@"mirror mode forced ON");
32
+ } else if (!strcasecmp(m, "off") || !strcmp(m, "0") || !strcasecmp(m, "false")) {
33
+ gMirrorMode = SimCamMirrorForceOff;
34
+ simcam_log(@"mirror mode forced OFF");
35
+ } else if (!strcasecmp(m, "auto")) {
36
+ gMirrorMode = SimCamMirrorAuto;
37
+ }
38
+ }
39
+
40
+ #pragma mark - Position tag tracking
41
+
42
+ static char kSimCamPositionKey;
43
+
44
+ AVCaptureDevicePosition SimCamPositionOf(id obj) {
45
+ if (!obj) return AVCaptureDevicePositionFront;
46
+ NSNumber *n = objc_getAssociatedObject(obj, &kSimCamPositionKey);
47
+ return n ? (AVCaptureDevicePosition)n.intValue : AVCaptureDevicePositionFront;
48
+ }
49
+ void SimCamSetPosition(id obj, AVCaptureDevicePosition p) {
50
+ objc_setAssociatedObject(obj, &kSimCamPositionKey, @(p), OBJC_ASSOCIATION_RETAIN);
51
+ }
52
+
53
+ #pragma mark - Camera-in-use sticky flag
54
+
55
+ static atomic_int gSimCamCameraInUse = 0;
56
+ static char kSimCamSessionUsingFakeCameraKey;
57
+
58
+ BOOL SimCamCameraIsInUse(void) {
59
+ return atomic_load_explicit(&gSimCamCameraInUse, memory_order_relaxed) > 0;
60
+ }
61
+ void SimCamMarkCameraInUse(void) {
62
+ atomic_store_explicit(&gSimCamCameraInUse, 1, memory_order_relaxed);
63
+ }
64
+ void SimCamMarkSessionUsingFakeCamera(id session, BOOL usingFakeCamera) {
65
+ if (!session) return;
66
+ objc_setAssociatedObject(session,
67
+ &kSimCamSessionUsingFakeCameraKey,
68
+ usingFakeCamera ? @YES : nil,
69
+ OBJC_ASSOCIATION_RETAIN);
70
+ }
71
+ static BOOL SimCamSessionUsesFakeCamera(id session) {
72
+ if (!session) return NO;
73
+ if (![session isKindOfClass:[AVCaptureSession class]]) return NO;
74
+ return [objc_getAssociatedObject(session, &kSimCamSessionUsingFakeCameraKey) boolValue];
75
+ }
76
+
77
+ #pragma mark - AVF runtime-error notification suppression
78
+
79
+ BOOL SimCamShouldSwallowAVFRuntimeError(NSNotificationName name, id object) {
80
+ if (!name) return NO;
81
+ if (![name isEqualToString:AVCaptureSessionRuntimeErrorNotification]) return NO;
82
+ return SimCamSessionUsesFakeCamera(object);
83
+ }
84
+
85
+ void SimCamLogSwallowedRuntimeError(NSString *via, id object, NSDictionary *userInfo) {
86
+ NSError *err = userInfo[AVCaptureSessionErrorKey];
87
+ simcam_log(@"SWALLOW AVCaptureSessionRuntimeError via %@ object=%@ code=%ld domain=%@ desc=%@ underlying=%@",
88
+ via,
89
+ object ? NSStringFromClass([object class]) : @"<nil>",
90
+ (long)err.code,
91
+ err.domain ?: @"<nil>",
92
+ err.localizedDescription ?: @"<nil>",
93
+ err.userInfo[NSUnderlyingErrorKey] ?: @"<nil>");
94
+ }
95
+
96
+ #pragma mark - Weak delegate ref
97
+
98
+ @implementation SimCamWeakRef
99
+ @end
100
+
101
+ #pragma mark - SimCamFakeFrameRateRange
102
+
103
+ @interface SimCamFakeFrameRateRange : AVFrameRateRange
104
+ @end
105
+ @implementation SimCamFakeFrameRateRange
106
+ - (Float64)minFrameRate { return 1.0; }
107
+ - (Float64)maxFrameRate { return 60.0; }
108
+ - (CMTime)minFrameDuration { return CMTimeMake(1, 60); }
109
+ - (CMTime)maxFrameDuration { return CMTimeMake(1, 1); }
110
+ @end
111
+
112
+ #pragma mark - SimCamFakeFormat
113
+
114
+ @implementation SimCamFakeFormat {
115
+ CMVideoFormatDescriptionRef _fd;
116
+ NSArray<AVFrameRateRange *> *_ranges;
117
+ }
118
+ - (CMFormatDescriptionRef)formatDescription {
119
+ if (!_fd) {
120
+ CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
121
+ kCVPixelFormatType_32BGRA, 1280, 720, NULL, &_fd);
122
+ }
123
+ return _fd;
124
+ }
125
+ - (NSArray<AVFrameRateRange *> *)videoSupportedFrameRateRanges {
126
+ if (!_ranges) {
127
+ AVFrameRateRange *r = (AVFrameRateRange *)class_createInstance(
128
+ [SimCamFakeFrameRateRange class], 0);
129
+ _ranges = r ? @[r] : @[];
130
+ }
131
+ return _ranges;
132
+ }
133
+ - (NSString *)mediaType { return AVMediaTypeVideo; }
134
+ - (FourCharCode)mediaSubType { return kCVPixelFormatType_32BGRA; }
135
+ - (CMVideoDimensions)highResolutionStillImageDimensions {
136
+ return (CMVideoDimensions){ 1280, 720 };
137
+ }
138
+ - (NSArray<NSValue *> *)supportedMaxPhotoDimensions {
139
+ CMVideoDimensions dims = { 1920, 1080 };
140
+ return @[ [NSValue valueWithBytes:&dims objCType:@encode(CMVideoDimensions)] ];
141
+ }
142
+ - (BOOL)isHighestPhotoQualitySupported { return YES; }
143
+ - (BOOL)isVideoBinned { return NO; }
144
+ - (BOOL)isVideoStabilizationModeSupported:(AVCaptureVideoStabilizationMode)m { return NO; }
145
+ - (CGFloat)videoMaxZoomFactor { return 16.0; }
146
+ - (CGFloat)videoZoomFactorUpscaleThreshold { return 1.0; }
147
+ - (AVCaptureAutoFocusSystem)autoFocusSystem { return AVCaptureAutoFocusSystemNone; }
148
+ - (BOOL)isMultiCamSupported { return NO; }
149
+ - (NSArray *)supportedColorSpaces { return @[]; }
150
+ - (NSArray *)supportedDepthDataFormats { return @[]; }
151
+ - (BOOL)isPortraitEffectSupported { return NO; }
152
+ - (NSArray<Class> *)unsupportedCaptureOutputClasses { return @[]; }
153
+ - (BOOL)isStreamingDisparitySupported { return NO; }
154
+ - (float)minISO { return 25.0f; }
155
+ - (float)maxISO { return 6400.0f; }
156
+ - (CMTime)minExposureDuration { return CMTimeMake(1, 8000); }
157
+ - (CMTime)maxExposureDuration { return CMTimeMake(1, 30); }
158
+ - (id)figCaptureSourceVideoFormat { return nil; }
159
+ - (void)dealloc { if (_fd) CFRelease(_fd); }
160
+ @end
161
+
162
+ AVCaptureDeviceFormat *SimCamSharedFakeFormat(void) {
163
+ static AVCaptureDeviceFormat *f = nil;
164
+ static dispatch_once_t once;
165
+ dispatch_once(&once, ^{
166
+ f = (AVCaptureDeviceFormat *)class_createInstance([SimCamFakeFormat class], 0);
167
+ });
168
+ return f;
169
+ }
170
+
171
+ #pragma mark - SimCamFakeDevice
172
+
173
+ static char kFakePositionKey;
174
+
175
+ @implementation SimCamFakeDevice
176
+ - (AVCaptureDevicePosition)position {
177
+ NSNumber *n = objc_getAssociatedObject(self, &kFakePositionKey);
178
+ return n ? (AVCaptureDevicePosition)n.intValue : AVCaptureDevicePositionFront;
179
+ }
180
+ - (NSString *)uniqueID {
181
+ return self.position == AVCaptureDevicePositionBack
182
+ ? @"sim-cam-fake-back-0" : @"sim-cam-fake-front-0";
183
+ }
184
+ - (NSString *)modelID { return @"SimCamFakeCamera"; }
185
+ - (NSString *)localizedName {
186
+ return self.position == AVCaptureDevicePositionBack
187
+ ? @"Simulated Camera Back (serve-sim)"
188
+ : @"Simulated Camera Front (serve-sim)";
189
+ }
190
+ - (NSString *)manufacturer { return @"serve-sim"; }
191
+ - (BOOL)hasMediaType:(AVMediaType)mediaType { return [mediaType isEqualToString:AVMediaTypeVideo]; }
192
+ - (BOOL)supportsAVCaptureSessionPreset:(AVCaptureSessionPreset)preset { return YES; }
193
+ - (AVCaptureDeviceType)deviceType { return AVCaptureDeviceTypeBuiltInWideAngleCamera; }
194
+ - (NSArray<AVCaptureDeviceFormat *> *)formats {
195
+ AVCaptureDeviceFormat *f = SimCamSharedFakeFormat();
196
+ return f ? @[f] : @[];
197
+ }
198
+ - (BOOL)isConnected { return YES; }
199
+ - (BOOL)isSuspended { return NO; }
200
+ - (BOOL)lockForConfiguration:(NSError **)e { return YES; }
201
+ - (void)unlockForConfiguration { }
202
+ - (AVCaptureDeviceFormat *)activeFormat { return SimCamSharedFakeFormat(); }
203
+ - (CMTime)activeVideoMinFrameDuration { return CMTimeMake(1, 30); }
204
+ - (CMTime)activeVideoMaxFrameDuration { return CMTimeMake(1, 30); }
205
+ - (CGFloat)videoZoomFactor { return 1.0; }
206
+ - (void)setVideoZoomFactor:(CGFloat)v { (void)v; }
207
+ - (void)rampToVideoZoomFactor:(CGFloat)f withRate:(float)r { (void)f; (void)r; }
208
+ - (void)cancelVideoZoomRamp { }
209
+ - (BOOL)isRampingVideoZoom { return NO; }
210
+ - (CGFloat)minAvailableVideoZoomFactor { return 1.0; }
211
+ - (CGFloat)maxAvailableVideoZoomFactor { return 16.0; }
212
+ - (CGFloat)dualCameraSwitchOverVideoZoomFactor { return 2.0; }
213
+ - (NSArray<NSNumber *> *)virtualDeviceSwitchOverVideoZoomFactors { return @[]; }
214
+ - (NSArray *)constituentDevices { return @[]; }
215
+ - (BOOL)isVirtualDevice { return NO; }
216
+ - (BOOL)hasTorch { return NO; }
217
+ - (BOOL)hasFlash { return NO; }
218
+ - (BOOL)isTorchAvailable { return NO; }
219
+ - (BOOL)isTorchActive { return NO; }
220
+ - (AVCaptureTorchMode)torchMode { return AVCaptureTorchModeOff; }
221
+ - (void)setTorchMode:(AVCaptureTorchMode)m { (void)m; }
222
+ - (BOOL)isTorchModeSupported:(AVCaptureTorchMode)m { (void)m; return NO; }
223
+ - (BOOL)setTorchModeOnWithLevel:(float)l error:(NSError **)e { (void)l; if (e) *e = nil; return YES; }
224
+ - (AVCaptureFocusMode)focusMode { return AVCaptureFocusModeContinuousAutoFocus; }
225
+ - (void)setFocusMode:(AVCaptureFocusMode)m { (void)m; }
226
+ - (BOOL)isFocusModeSupported:(AVCaptureFocusMode)m { (void)m; return YES; }
227
+ - (CGPoint)focusPointOfInterest { return CGPointMake(0.5, 0.5); }
228
+ - (void)setFocusPointOfInterest:(CGPoint)p { (void)p; }
229
+ - (BOOL)isFocusPointOfInterestSupported { return YES; }
230
+ - (BOOL)isAdjustingFocus { return NO; }
231
+ - (BOOL)isSmoothAutoFocusEnabled { return NO; }
232
+ - (void)setSmoothAutoFocusEnabled:(BOOL)b { (void)b; }
233
+ - (BOOL)isSmoothAutoFocusSupported { return NO; }
234
+ - (AVCaptureAutoFocusRangeRestriction)autoFocusRangeRestriction { return AVCaptureAutoFocusRangeRestrictionNone; }
235
+ - (void)setAutoFocusRangeRestriction:(AVCaptureAutoFocusRangeRestriction)r { (void)r; }
236
+ - (BOOL)isAutoFocusRangeRestrictionSupported { return NO; }
237
+ - (AVCaptureExposureMode)exposureMode { return AVCaptureExposureModeContinuousAutoExposure; }
238
+ - (void)setExposureMode:(AVCaptureExposureMode)m { (void)m; }
239
+ - (BOOL)isExposureModeSupported:(AVCaptureExposureMode)m { (void)m; return YES; }
240
+ - (CGPoint)exposurePointOfInterest { return CGPointMake(0.5, 0.5); }
241
+ - (void)setExposurePointOfInterest:(CGPoint)p { (void)p; }
242
+ - (BOOL)isExposurePointOfInterestSupported { return YES; }
243
+ - (BOOL)isAdjustingExposure { return NO; }
244
+ - (float)exposureTargetBias { return 0.0f; }
245
+ - (float)minExposureTargetBias { return -8.0f; }
246
+ - (float)maxExposureTargetBias { return 8.0f; }
247
+ - (CMTime)exposureDuration { return CMTimeMake(1, 30); }
248
+ - (float)ISO { return 100.0f; }
249
+ - (float)minISO { return 25.0f; }
250
+ - (float)maxISO { return 6400.0f; }
251
+ - (CMTime)activeMinExposureDuration { return CMTimeMake(1, 8000); }
252
+ - (CMTime)activeMaxExposureDuration { return CMTimeMake(1, 30); }
253
+ - (AVCaptureWhiteBalanceMode)whiteBalanceMode { return AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance; }
254
+ - (void)setWhiteBalanceMode:(AVCaptureWhiteBalanceMode)m { (void)m; }
255
+ - (BOOL)isWhiteBalanceModeSupported:(AVCaptureWhiteBalanceMode)m { (void)m; return YES; }
256
+ - (BOOL)isAdjustingWhiteBalance { return NO; }
257
+ - (BOOL)isFlashAvailable { return NO; }
258
+ - (BOOL)videoHDREnabled { return NO; }
259
+ - (void)setVideoHDREnabled:(BOOL)b { (void)b; }
260
+ - (BOOL)automaticallyAdjustsVideoHDREnabled { return NO; }
261
+ - (void)setAutomaticallyAdjustsVideoHDREnabled:(BOOL)b { (void)b; }
262
+ - (BOOL)isLowLightBoostSupported { return NO; }
263
+ - (BOOL)isLowLightBoostEnabled { return NO; }
264
+ - (BOOL)automaticallyEnablesLowLightBoostWhenAvailable { return NO; }
265
+ - (void)setAutomaticallyEnablesLowLightBoostWhenAvailable:(BOOL)b { (void)b; }
266
+ - (NSArray *)linkedDevices { return @[]; }
267
+ @end
268
+
269
+ AVCaptureDevice *SimCamFakeDeviceForPosition(AVCaptureDevicePosition p) {
270
+ static AVCaptureDevice *front = nil;
271
+ static AVCaptureDevice *back = nil;
272
+ static dispatch_once_t once;
273
+ dispatch_once(&once, ^{
274
+ front = (AVCaptureDevice *)class_createInstance([SimCamFakeDevice class], 0);
275
+ objc_setAssociatedObject(front, &kFakePositionKey,
276
+ @(AVCaptureDevicePositionFront), OBJC_ASSOCIATION_RETAIN);
277
+ back = (AVCaptureDevice *)class_createInstance([SimCamFakeDevice class], 0);
278
+ objc_setAssociatedObject(back, &kFakePositionKey,
279
+ @(AVCaptureDevicePositionBack), OBJC_ASSOCIATION_RETAIN);
280
+ });
281
+ return p == AVCaptureDevicePositionBack ? back : front;
282
+ }
283
+
284
+ #pragma mark - SimCamFakeConnection
285
+
286
+ @interface AVCaptureConnection (SimCamPrivate)
287
+ - (BOOL)sourcesFromExternalCamera;
288
+ - (AVCaptureVideoOrientation)_videoOrientation;
289
+ @end
290
+
291
+ static AVCaptureInputPort *SimCamFakeInputPortForInput(AVCaptureInput *input, AVCaptureDevicePosition position);
292
+
293
+ @implementation SimCamFakeConnection {
294
+ __weak AVCaptureOutput *_outputRef;
295
+ AVCaptureDevicePosition _position;
296
+ AVCaptureVideoOrientation _orientation;
297
+ BOOL _videoMirrored;
298
+ BOOL _automaticallyAdjustsVideoMirroring;
299
+ BOOL _enabled;
300
+ }
301
+ + (instancetype)allocWithZone:(NSZone *)zone {
302
+ return class_createInstance([SimCamFakeConnection class], 0);
303
+ }
304
+ + (instancetype)connectionForOutput:(AVCaptureOutput *)output
305
+ position:(AVCaptureDevicePosition)pos {
306
+ SimCamFakeConnection *c = [self alloc];
307
+ if (c) {
308
+ c->_outputRef = output;
309
+ c->_position = pos;
310
+ c->_orientation = AVCaptureVideoOrientationPortrait;
311
+ c->_automaticallyAdjustsVideoMirroring = YES;
312
+ c->_videoMirrored = SimCamShouldMirror(pos);
313
+ c->_enabled = YES;
314
+ }
315
+ return c;
316
+ }
317
+ - (AVCaptureOutput *)output { return _outputRef; }
318
+ - (NSArray *)inputPorts {
319
+ AVCaptureInput *input = SimCamOutputInput(_outputRef) ?: SimCamFakeInputForPosition(_position);
320
+ AVCaptureInputPort *port = SimCamFakeInputPortForInput(input, _position);
321
+ return port ? @[port] : @[];
322
+ }
323
+ - (AVCaptureInput *)input { return SimCamOutputInput(_outputRef) ?: SimCamFakeInputForPosition(_position); }
324
+ - (AVCaptureVideoPreviewLayer *)videoPreviewLayer { return nil; }
325
+ - (BOOL)isEnabled { return _enabled; }
326
+ - (void)setEnabled:(BOOL)e { _enabled = e; }
327
+ - (BOOL)isActive { return YES; }
328
+ - (NSArray *)audioChannels { return @[]; }
329
+ - (AVMediaType)mediaType { return AVMediaTypeVideo; }
330
+
331
+ - (AVCaptureDevice *)sourceDevice { return SimCamFakeDeviceForPosition(_position); }
332
+ - (AVCaptureDeviceType)sourceDeviceType { return AVCaptureDeviceTypeBuiltInWideAngleCamera; }
333
+ - (AVCaptureDevicePosition)sourceDevicePosition { return _position; }
334
+ - (AVCaptureSession *)originatingSession { return nil; }
335
+
336
+ - (AVCaptureDeviceInput *)deviceInput {
337
+ return SimCamFakeInputForPosition(_position);
338
+ }
339
+
340
+ - (BOOL)isVideoOrientationSupported { return YES; }
341
+ - (AVCaptureVideoOrientation)videoOrientation { return _orientation; }
342
+ - (void)setVideoOrientation:(AVCaptureVideoOrientation)o { _orientation = o; }
343
+
344
+ - (BOOL)isVideoRotationAngleSupported:(CGFloat)angle { (void)angle; return YES; }
345
+ - (CGFloat)videoRotationAngle {
346
+ switch (_orientation) {
347
+ case AVCaptureVideoOrientationPortrait: return 90.0;
348
+ case AVCaptureVideoOrientationPortraitUpsideDown: return 270.0;
349
+ case AVCaptureVideoOrientationLandscapeRight: return 0.0;
350
+ case AVCaptureVideoOrientationLandscapeLeft: return 180.0;
351
+ default: return 90.0;
352
+ }
353
+ }
354
+ - (void)setVideoRotationAngle:(CGFloat)angle {
355
+ long a = ((long)angle % 360 + 360) % 360;
356
+ if (a == 0) _orientation = AVCaptureVideoOrientationLandscapeRight;
357
+ else if (a == 90) _orientation = AVCaptureVideoOrientationPortrait;
358
+ else if (a == 180) _orientation = AVCaptureVideoOrientationLandscapeLeft;
359
+ else if (a == 270) _orientation = AVCaptureVideoOrientationPortraitUpsideDown;
360
+ }
361
+
362
+ - (BOOL)isVideoMirroringSupported { return YES; }
363
+ - (BOOL)isVideoMirrored {
364
+ if (_automaticallyAdjustsVideoMirroring) return SimCamShouldMirror(_position);
365
+ return _videoMirrored;
366
+ }
367
+ - (void)setVideoMirrored:(BOOL)m {
368
+ _videoMirrored = m;
369
+ _automaticallyAdjustsVideoMirroring = NO;
370
+ }
371
+ - (BOOL)automaticallyAdjustsVideoMirroring { return _automaticallyAdjustsVideoMirroring; }
372
+ - (void)setAutomaticallyAdjustsVideoMirroring:(BOOL)b {
373
+ _automaticallyAdjustsVideoMirroring = b;
374
+ if (b) _videoMirrored = SimCamShouldMirror(_position);
375
+ }
376
+
377
+ - (BOOL)isVideoMinFrameDurationSupported { return NO; }
378
+ - (BOOL)isVideoMaxFrameDurationSupported { return NO; }
379
+ - (CMTime)videoMinFrameDuration { return kCMTimeInvalid; }
380
+ - (CMTime)videoMaxFrameDuration { return kCMTimeInvalid; }
381
+ - (void)setVideoMinFrameDuration:(CMTime)d { (void)d; }
382
+ - (void)setVideoMaxFrameDuration:(CMTime)d { (void)d; }
383
+
384
+ - (BOOL)isVideoStabilizationSupported { return NO; }
385
+ - (AVCaptureVideoStabilizationMode)preferredVideoStabilizationMode { return AVCaptureVideoStabilizationModeOff; }
386
+ - (void)setPreferredVideoStabilizationMode:(AVCaptureVideoStabilizationMode)m { (void)m; }
387
+ - (AVCaptureVideoStabilizationMode)activeVideoStabilizationMode { return AVCaptureVideoStabilizationModeOff; }
388
+ - (BOOL)isVideoStabilizationEnabled { return NO; }
389
+ - (BOOL)enablesVideoStabilizationWhenAvailable { return NO; }
390
+ - (void)setEnablesVideoStabilizationWhenAvailable:(BOOL)b { (void)b; }
391
+
392
+ - (BOOL)isCameraIntrinsicMatrixDeliverySupported { return NO; }
393
+ - (BOOL)isCameraIntrinsicMatrixDeliveryEnabled { return NO; }
394
+ - (void)setCameraIntrinsicMatrixDeliveryEnabled:(BOOL)b { (void)b; }
395
+
396
+ - (BOOL)isVideoFieldModeSupported { return NO; }
397
+ - (CGFloat)videoMaxScaleAndCropFactor { return 1.0; }
398
+ - (CGFloat)videoScaleAndCropFactor { return 1.0; }
399
+ - (void)setVideoScaleAndCropFactor:(CGFloat)v { (void)v; }
400
+
401
+ - (AVCaptureVideoOrientation)_videoOrientation { return _orientation; }
402
+ - (BOOL)sourcesFromExternalCamera { return NO; }
403
+ @end
404
+
405
+ static char kSimCamOutputConnectionKey;
406
+
407
+ AVCaptureConnection *SimCamFakeConnectionForOutput(AVCaptureOutput *out) {
408
+ if (!out) return nil;
409
+ AVCaptureConnection *conn = objc_getAssociatedObject(out, &kSimCamOutputConnectionKey);
410
+ if (!conn) {
411
+ conn = (AVCaptureConnection *)[SimCamFakeConnection
412
+ connectionForOutput:out
413
+ position:SimCamPositionOf(out)];
414
+ if (conn) {
415
+ objc_setAssociatedObject(out, &kSimCamOutputConnectionKey, conn,
416
+ OBJC_ASSOCIATION_RETAIN);
417
+ }
418
+ }
419
+ return conn;
420
+ }
421
+
422
+ #pragma mark - SimCamFakeInputPort
423
+
424
+ @interface SimCamFakeInputPort : AVCaptureInputPort
425
+ @end
426
+
427
+ @implementation SimCamFakeInputPort {
428
+ __weak AVCaptureInput *_inputRef;
429
+ AVCaptureDevicePosition _position;
430
+ }
431
+ + (instancetype)allocWithZone:(NSZone *)zone {
432
+ return class_createInstance([SimCamFakeInputPort class], 0);
433
+ }
434
+ + (instancetype)portForInput:(AVCaptureInput *)input position:(AVCaptureDevicePosition)position {
435
+ SimCamFakeInputPort *p = [self alloc];
436
+ if (p) {
437
+ p->_inputRef = input;
438
+ p->_position = position;
439
+ }
440
+ return p;
441
+ }
442
+ - (AVCaptureInput *)input { return _inputRef; }
443
+ - (AVMediaType)mediaType { return AVMediaTypeVideo; }
444
+ - (AVCaptureDeviceType)sourceDeviceType { return AVCaptureDeviceTypeBuiltInWideAngleCamera; }
445
+ - (AVCaptureDevicePosition)sourceDevicePosition { return _position; }
446
+ - (CMFormatDescriptionRef)formatDescription { return SimCamSharedFakeFormat().formatDescription; }
447
+ - (BOOL)isEnabled { return YES; }
448
+ - (void)setEnabled:(BOOL)enabled { (void)enabled; }
449
+ @end
450
+
451
+ static char kSimCamOutputInputRefKey;
452
+ static char kSimCamFakeInputPortKey;
453
+
454
+ void SimCamSetOutputInput(AVCaptureOutput *out, AVCaptureInput *input) {
455
+ if (!out) return;
456
+ if (!input) {
457
+ objc_setAssociatedObject(out, &kSimCamOutputInputRefKey, nil, OBJC_ASSOCIATION_RETAIN);
458
+ return;
459
+ }
460
+ SimCamWeakRef *ref = [SimCamWeakRef new];
461
+ ref.target = input;
462
+ objc_setAssociatedObject(out, &kSimCamOutputInputRefKey, ref, OBJC_ASSOCIATION_RETAIN);
463
+ }
464
+
465
+ AVCaptureInput *SimCamOutputInput(AVCaptureOutput *out) {
466
+ if (!out) return nil;
467
+ SimCamWeakRef *ref = objc_getAssociatedObject(out, &kSimCamOutputInputRefKey);
468
+ return ref.target;
469
+ }
470
+
471
+ static AVCaptureInputPort *SimCamFakeInputPortForInput(AVCaptureInput *input, AVCaptureDevicePosition position) {
472
+ if (!input) return nil;
473
+ AVCaptureInputPort *port = objc_getAssociatedObject(input, &kSimCamFakeInputPortKey);
474
+ if (!port) {
475
+ port = (AVCaptureInputPort *)[SimCamFakeInputPort portForInput:input position:position];
476
+ if (port) {
477
+ objc_setAssociatedObject(input, &kSimCamFakeInputPortKey, port, OBJC_ASSOCIATION_RETAIN);
478
+ }
479
+ }
480
+ return port;
481
+ }
482
+
483
+ #pragma mark - SimCamFakeInput marking
484
+
485
+ static char kSimCamFakeInputKey;
486
+ static char kSimCamFakeInputDeviceKey;
487
+
488
+ void SimCamMarkFakeInput(id input, AVCaptureDevice *fakeDevice) {
489
+ if (!input) return;
490
+ objc_setAssociatedObject(input, &kSimCamFakeInputKey, @YES, OBJC_ASSOCIATION_RETAIN);
491
+ if (fakeDevice) {
492
+ objc_setAssociatedObject(input, &kSimCamFakeInputDeviceKey, fakeDevice, OBJC_ASSOCIATION_RETAIN);
493
+ }
494
+ }
495
+ BOOL SimCamIsFakeInput(id input) {
496
+ if (!input) return NO;
497
+ return [objc_getAssociatedObject(input, &kSimCamFakeInputKey) boolValue];
498
+ }
499
+ AVCaptureDevice *SimCamFakeInputDevice(id input) {
500
+ if (!input) return nil;
501
+ return objc_getAssociatedObject(input, &kSimCamFakeInputDeviceKey);
502
+ }
503
+
504
+ AVCaptureDeviceInput *SimCamFakeInputForPosition(AVCaptureDevicePosition p) {
505
+ static AVCaptureDeviceInput *front = nil;
506
+ static AVCaptureDeviceInput *back = nil;
507
+ static dispatch_once_t once;
508
+ dispatch_once(&once, ^{
509
+ front = (AVCaptureDeviceInput *)class_createInstance([AVCaptureDeviceInput class], 0);
510
+ SimCamMarkFakeInput(front, SimCamFakeDeviceForPosition(AVCaptureDevicePositionFront));
511
+ SimCamSetPosition(front, AVCaptureDevicePositionFront);
512
+
513
+ back = (AVCaptureDeviceInput *)class_createInstance([AVCaptureDeviceInput class], 0);
514
+ SimCamMarkFakeInput(back, SimCamFakeDeviceForPosition(AVCaptureDevicePositionBack));
515
+ SimCamSetPosition(back, AVCaptureDevicePositionBack);
516
+ });
517
+ return p == AVCaptureDevicePositionBack ? back : front;
518
+ }
519
+
520
+ #pragma mark - SimCamFakeResolvedPhotoSettings
521
+
522
+ @interface SimCamFakeResolvedPhotoSettings (SimCamFactory)
523
+ + (instancetype)settingsWithDimensions:(CMVideoDimensions)dims;
524
+ @end
525
+
526
+ @implementation SimCamFakeResolvedPhotoSettings {
527
+ CMVideoDimensions _photoDims;
528
+ }
529
+ + (instancetype)allocWithZone:(NSZone *)zone {
530
+ return class_createInstance([SimCamFakeResolvedPhotoSettings class], 0);
531
+ }
532
+ + (instancetype)settingsWithDimensions:(CMVideoDimensions)dims {
533
+ SimCamFakeResolvedPhotoSettings *s = [self alloc];
534
+ if (s) s->_photoDims = dims;
535
+ return s;
536
+ }
537
+ - (CMVideoDimensions)photoDimensions { return _photoDims; }
538
+ - (CMVideoDimensions)rawPhotoDimensions { return (CMVideoDimensions){0, 0}; }
539
+ - (CMVideoDimensions)previewDimensions { return _photoDims; }
540
+ - (CMVideoDimensions)embeddedThumbnailDimensions { return (CMVideoDimensions){0, 0}; }
541
+ - (CMVideoDimensions)portraitEffectsMatteDimensions { return (CMVideoDimensions){0, 0}; }
542
+ - (CMVideoDimensions)rawEmbeddedThumbnailDimensions { return (CMVideoDimensions){0, 0}; }
543
+ - (int64_t)uniqueID { return 1; }
544
+ - (BOOL)isFlashEnabled { return NO; }
545
+ - (BOOL)isRedEyeReductionEnabled { return NO; }
546
+ - (BOOL)isContentAwareDistortionCorrectionEnabled { return NO; }
547
+ - (BOOL)isStillImageStabilizationEnabled { return NO; }
548
+ - (BOOL)isVirtualDeviceFusionEnabled { return NO; }
549
+ - (BOOL)isAutoVirtualDeviceFusionEnabled { return NO; }
550
+ - (BOOL)isDualCameraFusionEnabled { return NO; }
551
+ - (BOOL)isAutoDualCameraFusionEnabled { return NO; }
552
+ - (BOOL)isDepthDataDeliveryEnabled { return NO; }
553
+ - (BOOL)isPortraitEffectsMatteDeliveryEnabled { return NO; }
554
+ - (BOOL)isCameraCalibrationDataDeliveryEnabled { return NO; }
555
+ - (NSArray *)enabledSemanticSegmentationMatteTypes { return @[]; }
556
+ - (CMTimeRange)photoProcessingTimeRange {
557
+ return CMTimeRangeMake(kCMTimeZero, kCMTimeZero);
558
+ }
559
+ - (CMTime)expectedPhotoCaptureDuration { return kCMTimeInvalid; }
560
+ - (NSURL *)deferredPhotoProxyDataFileURL { return nil; }
561
+ - (NSDictionary *)dimensionsRepresentation { return @{}; }
562
+ @end
563
+
564
+ #pragma mark - SimCamFakePhoto
565
+
566
+ @implementation SimCamFakePhoto {
567
+ NSData *_jpegData;
568
+ CGImageRef _cgImage;
569
+ NSDictionary *_metadata;
570
+ AVCaptureResolvedPhotoSettings *_resolvedSettings;
571
+ }
572
+ + (instancetype)allocWithZone:(NSZone *)zone {
573
+ return class_createInstance([SimCamFakePhoto class], 0);
574
+ }
575
+ + (instancetype)photoFromImage:(CGImageRef)cgImage
576
+ jpegQuality:(CGFloat)q
577
+ mirrored:(BOOL)mirrored {
578
+ if (!cgImage) return nil;
579
+ SimCamFakePhoto *p = [SimCamFakePhoto alloc];
580
+ if (p) {
581
+ p->_cgImage = CGImageRetain(cgImage);
582
+ UIImage *ui = [UIImage imageWithCGImage:cgImage];
583
+ p->_jpegData = UIImageJPEGRepresentation(ui, q);
584
+ UInt32 exifOrient = mirrored ? 2u : 1u;
585
+ p->_metadata = @{
586
+ (NSString *)kCGImagePropertyOrientation: @(exifOrient),
587
+ };
588
+ CMVideoDimensions dims = {
589
+ (int32_t)CGImageGetWidth(cgImage),
590
+ (int32_t)CGImageGetHeight(cgImage),
591
+ };
592
+ p->_resolvedSettings = (AVCaptureResolvedPhotoSettings *)
593
+ [SimCamFakeResolvedPhotoSettings settingsWithDimensions:dims];
594
+ }
595
+ return p;
596
+ }
597
+ - (NSData *)fileDataRepresentation { return _jpegData; }
598
+ - (NSData *)fileDataRepresentationWithCustomizer:(id)c { return _jpegData; }
599
+ - (NSData *)fileDataRepresentationWithReplacementMetadata:(NSDictionary *)m
600
+ replacementEmbeddedThumbnailPhotoFormat:(NSDictionary *)t
601
+ replacementEmbeddedThumbnailPixelBuffer:(CVPixelBufferRef)pb
602
+ replacementDepthData:(id)d { return _jpegData; }
603
+ - (CGImageRef)CGImageRepresentation { return _cgImage; }
604
+ - (CGImageRef)previewCGImageRepresentation { return _cgImage; }
605
+ - (NSDictionary *)metadata { return _metadata; }
606
+ - (CVPixelBufferRef)pixelBuffer { return NULL; }
607
+ - (CVPixelBufferRef)previewPixelBuffer { return NULL; }
608
+ - (AVDepthData *)depthData { return nil; }
609
+ - (AVCameraCalibrationData *)cameraCalibrationData { return nil; }
610
+ - (NSData *)bracketSettings { return nil; }
611
+ - (AVCaptureBracketedStillImageSettings *)bracketedSettings { return nil; }
612
+ - (NSData *)embeddedThumbnailPhotoFormat { return nil; }
613
+ - (NSInteger)photoCount { return 1; }
614
+ - (NSInteger)sequenceCount { return 1; }
615
+ - (CMTime)timestamp { return CMTimeMake(0, 30); }
616
+ - (BOOL)isRawPhoto { return NO; }
617
+ - (AVCaptureResolvedPhotoSettings *)resolvedSettings { return _resolvedSettings; }
618
+ - (NSString *)sourceDeviceType { return AVCaptureDeviceTypeBuiltInWideAngleCamera; }
619
+ - (NSArray *)availableRawEmbeddedThumbnailPhotoCodecTypes { return @[]; }
620
+ - (NSArray *)availableEmbeddedThumbnailPhotoCodecTypes { return @[]; }
621
+ - (void)dealloc { if (_cgImage) CGImageRelease(_cgImage); }
622
+ @end
623
+
624
+ #pragma mark - CoreMotion fakes
625
+
626
+ @implementation SimCamAttitude
627
+ - (double)pitch { return 0.0; }
628
+ - (double)roll { return 0.0; }
629
+ - (double)yaw { return 0.0; }
630
+ - (CMRotationMatrix)rotationMatrix {
631
+ return (CMRotationMatrix){ 1, 0, 0, 0, 1, 0, 0, 0, 1 };
632
+ }
633
+ - (CMQuaternion)quaternion { return (CMQuaternion){ 0, 0, 0, 1 }; }
634
+ - (void)multiplyByInverseOfAttitude:(CMAttitude *)attitude { (void)attitude; }
635
+ @end
636
+
637
+ @implementation SimCamAccelerometerData
638
+ - (CMAcceleration)acceleration { return (CMAcceleration){ 0.0, -1.0, 0.0 }; }
639
+ - (NSTimeInterval)timestamp { return [NSProcessInfo processInfo].systemUptime; }
640
+ @end
641
+
642
+ @implementation SimCamGyroData
643
+ - (CMRotationRate)rotationRate { return (CMRotationRate){ 0.0, 0.0, 0.0 }; }
644
+ - (NSTimeInterval)timestamp { return [NSProcessInfo processInfo].systemUptime; }
645
+ @end
646
+
647
+ @implementation SimCamMagnetometerData
648
+ - (CMMagneticField)magneticField { return (CMMagneticField){ 0.0, 0.0, 0.0 }; }
649
+ - (NSTimeInterval)timestamp { return [NSProcessInfo processInfo].systemUptime; }
650
+ @end
651
+
652
+ @implementation SimCamDeviceMotion
653
+ - (CMAttitude *)attitude { return SimCamSharedAttitude(); }
654
+ - (CMAcceleration)gravity { return (CMAcceleration){ 0.0, -1.0, 0.0 }; }
655
+ - (CMAcceleration)userAcceleration { return (CMAcceleration){ 0.0, 0.0, 0.0 }; }
656
+ - (CMRotationRate)rotationRate { return (CMRotationRate){ 0.0, 0.0, 0.0 }; }
657
+ - (CMCalibratedMagneticField)magneticField {
658
+ return (CMCalibratedMagneticField){ { 0.0, 0.0, 0.0 },
659
+ CMMagneticFieldCalibrationAccuracyUncalibrated };
660
+ }
661
+ - (double)heading { return 0.0; }
662
+ - (NSTimeInterval)timestamp { return [NSProcessInfo processInfo].systemUptime; }
663
+ @end
664
+
665
+ CMAttitude *SimCamSharedAttitude(void) {
666
+ static CMAttitude *att = nil;
667
+ static dispatch_once_t once;
668
+ dispatch_once(&once, ^{
669
+ att = (CMAttitude *)class_createInstance([SimCamAttitude class], 0);
670
+ });
671
+ return att;
672
+ }
673
+ CMAccelerometerData *SimCamSharedAccelerometerData(void) {
674
+ static CMAccelerometerData *d = nil;
675
+ static dispatch_once_t once;
676
+ dispatch_once(&once, ^{
677
+ d = (CMAccelerometerData *)class_createInstance([SimCamAccelerometerData class], 0);
678
+ });
679
+ return d;
680
+ }
681
+ CMGyroData *SimCamSharedGyroData(void) {
682
+ static CMGyroData *d = nil;
683
+ static dispatch_once_t once;
684
+ dispatch_once(&once, ^{
685
+ d = (CMGyroData *)class_createInstance([SimCamGyroData class], 0);
686
+ });
687
+ return d;
688
+ }
689
+ CMMagnetometerData *SimCamSharedMagnetometerData(void) {
690
+ static CMMagnetometerData *d = nil;
691
+ static dispatch_once_t once;
692
+ dispatch_once(&once, ^{
693
+ d = (CMMagnetometerData *)class_createInstance([SimCamMagnetometerData class], 0);
694
+ });
695
+ return d;
696
+ }
697
+ CMDeviceMotion *SimCamSharedDeviceMotion(void) {
698
+ static CMDeviceMotion *d = nil;
699
+ static dispatch_once_t once;
700
+ dispatch_once(&once, ^{
701
+ d = (CMDeviceMotion *)class_createInstance([SimCamDeviceMotion class], 0);
702
+ });
703
+ return d;
704
+ }