serve-sim 0.1.24 → 0.1.26
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.
- package/README.md +2 -2
- package/Sources/SimCameraHelper/build.sh +1 -1
- package/Sources/SimCameraHelper/main.m +28 -17
- package/Sources/SimCameraInjector/SimCamFakes.h +86 -0
- package/Sources/SimCameraInjector/SimCamFakes.m +635 -0
- package/Sources/SimCameraInjector/SimCamFrameSource.h +26 -0
- package/Sources/SimCameraInjector/SimCamFrameSource.m +546 -0
- package/Sources/SimCameraInjector/SimCamLog.h +5 -0
- package/Sources/SimCameraInjector/SimCamLog.m +9 -0
- package/Sources/SimCameraInjector/SimCamSwizzles.h +3 -0
- package/Sources/SimCameraInjector/SimCamSwizzles.m +1014 -0
- package/Sources/SimCameraInjector/SimCameraInjector.m +9 -1150
- package/Sources/SimCameraInjector/build.sh +6 -1
- package/bin/serve-sim-bin +0 -0
- package/dist/middleware.js +25 -25
- package/dist/serve-sim.js +37 -34
- package/dist/simcam/libSimCameraInjector.dylib +0 -0
- package/dist/simcam/serve-sim-camera-helper +0 -0
- package/package.json +3 -1
- package/src/middleware.ts +125 -25
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ I develop the Expo framework, but this tool is completely agnostic to React Nati
|
|
|
31
31
|
|
|
32
32
|
## Install
|
|
33
33
|
|
|
34
|
-
Requires macOS with Xcode command line tools (`xcrun simctl`) and Node.js 18+. `bun` is **not** required to run the CLI.
|
|
34
|
+
Requires macOS with Xcode command line tools (`xcrun simctl`) and Node.js 18+. `bun` is **not** required to run the CLI. Camera injection uses a host-side helper built for macOS 14+.
|
|
35
35
|
|
|
36
36
|
## CLI
|
|
37
37
|
|
|
@@ -137,7 +137,7 @@ Create a `.claude/launch.json` and define a server:
|
|
|
137
137
|
"name": "Apple",
|
|
138
138
|
"runtimeExecutable": "npx",
|
|
139
139
|
"runtimeArgs": ["serve-sim"],
|
|
140
|
-
"
|
|
140
|
+
"port": 3200
|
|
141
141
|
}
|
|
142
142
|
]
|
|
143
143
|
}
|
|
@@ -267,20 +267,30 @@ static void RenderPlaceholderFrame(uint8_t *out, uint64_t frameIdx) {
|
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
static void StartPlaceholderSource(void) {
|
|
270
|
+
static uint8_t *buf = NULL;
|
|
271
|
+
size_t need = (size_t)gWidth * gHeight * 4;
|
|
272
|
+
if (!buf) buf = calloc(1, need);
|
|
273
|
+
if (!buf) {
|
|
274
|
+
fprintf(stderr, "[serve-sim-camera] placeholder buf alloc failed (%zu bytes)\n", need);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
__block uint64_t frameIdx = 0;
|
|
279
|
+
RenderPlaceholderFrame(buf, frameIdx++);
|
|
280
|
+
PublishFrame(buf);
|
|
281
|
+
|
|
270
282
|
gPlaceholderTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
|
|
271
283
|
dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0));
|
|
272
284
|
uint64_t intervalNs = NSEC_PER_SEC / 30;
|
|
273
285
|
dispatch_source_set_timer(gPlaceholderTimer,
|
|
274
|
-
dispatch_time(DISPATCH_TIME_NOW,
|
|
275
|
-
static uint8_t *buf = NULL;
|
|
276
|
-
size_t need = (size_t)gWidth * gHeight * 4;
|
|
277
|
-
if (!buf) buf = malloc(need);
|
|
278
|
-
__block uint64_t frameIdx = 0;
|
|
286
|
+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)intervalNs), intervalNs, intervalNs / 10);
|
|
279
287
|
dispatch_source_set_event_handler(gPlaceholderTimer, ^{
|
|
280
288
|
RenderPlaceholderFrame(buf, frameIdx++);
|
|
281
289
|
PublishFrame(buf);
|
|
282
290
|
});
|
|
283
291
|
dispatch_resume(gPlaceholderTimer);
|
|
292
|
+
fprintf(stderr, "[serve-sim-camera] placeholder source running @ 30fps (%ux%u, first frame seq=%llu)\n",
|
|
293
|
+
gWidth, gHeight, (unsigned long long)atomic_load(&gFrameSeq));
|
|
284
294
|
}
|
|
285
295
|
|
|
286
296
|
static void StopPlaceholderSource(void) {
|
|
@@ -364,10 +374,10 @@ static BOOL StartImageSource(NSString *path, NSString **err) {
|
|
|
364
374
|
CGContextRef ctx = CGBitmapContextCreate(buf, gWidth, gHeight, 8, bpr, cs,
|
|
365
375
|
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
|
|
366
376
|
CGColorSpaceRelease(cs);
|
|
367
|
-
// Aspect-fill the source image into the destination buffer.
|
|
368
377
|
size_t iw = CGImageGetWidth(img), ih = CGImageGetHeight(img);
|
|
369
378
|
double sx = (double)gWidth / iw, sy = (double)gHeight / ih;
|
|
370
|
-
|
|
379
|
+
// Aspect-fit file sources so the full source frame remains visible.
|
|
380
|
+
double s = MIN(sx, sy);
|
|
371
381
|
double dw = iw * s, dh = ih * s;
|
|
372
382
|
CGContextDrawImage(ctx, CGRectMake((gWidth - dw)/2.0, (gHeight - dh)/2.0, dw, dh), img);
|
|
373
383
|
CGContextRelease(ctx);
|
|
@@ -425,7 +435,7 @@ static AVAssetReaderTrackOutput *MakeVideoOutput(AVAssetReader **outReader,
|
|
|
425
435
|
return out;
|
|
426
436
|
}
|
|
427
437
|
|
|
428
|
-
// Aspect-
|
|
438
|
+
// Aspect-fit a source pixel buffer into a transient BGRA buffer sized to
|
|
429
439
|
// the shm region. We allocate once per call so the caller is free to free
|
|
430
440
|
// the result without worrying about lifetime sharing.
|
|
431
441
|
static uint8_t *RenderPixelBufferToShmSize(CVPixelBufferRef pb) {
|
|
@@ -457,7 +467,8 @@ static uint8_t *RenderPixelBufferToShmSize(CVPixelBufferRef pb) {
|
|
|
457
467
|
CGDataProviderRelease(dp);
|
|
458
468
|
|
|
459
469
|
double sx = (double)gWidth / srcW, sy = (double)gHeight / srcH;
|
|
460
|
-
|
|
470
|
+
// Keep video file playback letterboxed instead of cropping the source.
|
|
471
|
+
double s = MIN(sx, sy);
|
|
461
472
|
double dw = srcW * s, dh = srcH * s;
|
|
462
473
|
CGContextDrawImage(ctx, CGRectMake((gWidth - dw)/2.0, (gHeight - dh)/2.0, dw, dh), img);
|
|
463
474
|
CGImageRelease(img);
|
|
@@ -820,14 +831,6 @@ int main(int argc, const char *argv[]) {
|
|
|
820
831
|
|
|
821
832
|
gSourceQueue = dispatch_queue_create("simcam.helper.source", DISPATCH_QUEUE_SERIAL);
|
|
822
833
|
|
|
823
|
-
if (socketPath) {
|
|
824
|
-
if (OpenControlSocket(socketPath) < 0) {
|
|
825
|
-
fprintf(stderr, "[serve-sim-camera] control socket open failed: %s\n", socketPath);
|
|
826
|
-
} else {
|
|
827
|
-
fprintf(stderr, "[serve-sim-camera] control socket %s\n", socketPath);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
834
|
SimCamSourceKind k = ParseSourceName(initialSource);
|
|
832
835
|
if (k == (SimCamSourceKind)-1) {
|
|
833
836
|
fprintf(stderr, "[serve-sim-camera] unknown --source %s, defaulting to placeholder\n",
|
|
@@ -841,6 +844,14 @@ int main(int argc, const char *argv[]) {
|
|
|
841
844
|
(void)SwitchSource(SimCamSourcePlaceholder, nil, NULL);
|
|
842
845
|
}
|
|
843
846
|
|
|
847
|
+
if (socketPath) {
|
|
848
|
+
if (OpenControlSocket(socketPath) < 0) {
|
|
849
|
+
fprintf(stderr, "[serve-sim-camera] control socket open failed: %s\n", socketPath);
|
|
850
|
+
} else {
|
|
851
|
+
fprintf(stderr, "[serve-sim-camera] control socket %s\n", socketPath);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
844
855
|
signal(SIGINT, HandleSig);
|
|
845
856
|
signal(SIGTERM, HandleSig);
|
|
846
857
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <AVFoundation/AVFoundation.h>
|
|
4
|
+
#import <CoreMotion/CoreMotion.h>
|
|
5
|
+
|
|
6
|
+
#pragma mark - Mirror mode
|
|
7
|
+
|
|
8
|
+
typedef NS_ENUM(NSInteger, SimCamMirrorMode) {
|
|
9
|
+
SimCamMirrorAuto = 0,
|
|
10
|
+
SimCamMirrorForceOn,
|
|
11
|
+
SimCamMirrorForceOff,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
SimCamMirrorMode SimCamGetMirrorMode(void);
|
|
15
|
+
void SimCamSetMirrorMode(SimCamMirrorMode m);
|
|
16
|
+
BOOL SimCamShouldMirror(AVCaptureDevicePosition p);
|
|
17
|
+
|
|
18
|
+
void SimCamReadMirrorModeFromEnv(void);
|
|
19
|
+
|
|
20
|
+
#pragma mark - Position tag
|
|
21
|
+
|
|
22
|
+
AVCaptureDevicePosition SimCamPositionOf(id obj);
|
|
23
|
+
void SimCamSetPosition(id obj, AVCaptureDevicePosition p);
|
|
24
|
+
|
|
25
|
+
#pragma mark - Camera-in-use sticky flag
|
|
26
|
+
|
|
27
|
+
BOOL SimCamCameraIsInUse(void);
|
|
28
|
+
void SimCamMarkCameraInUse(void);
|
|
29
|
+
void SimCamMarkSessionUsingFakeCamera(id session, BOOL usingFakeCamera);
|
|
30
|
+
|
|
31
|
+
#pragma mark - AVF runtime-error notification suppression
|
|
32
|
+
|
|
33
|
+
BOOL SimCamShouldSwallowAVFRuntimeError(NSNotificationName name, id object);
|
|
34
|
+
void SimCamLogSwallowedRuntimeError(NSString *via, id object, NSDictionary *userInfo);
|
|
35
|
+
|
|
36
|
+
#pragma mark - Fake objects
|
|
37
|
+
|
|
38
|
+
@interface SimCamWeakRef : NSObject
|
|
39
|
+
@property (nonatomic, weak) id target;
|
|
40
|
+
@end
|
|
41
|
+
|
|
42
|
+
@interface SimCamFakeFormat : AVCaptureDeviceFormat
|
|
43
|
+
@end
|
|
44
|
+
@interface SimCamFakeDevice : AVCaptureDevice
|
|
45
|
+
@end
|
|
46
|
+
@interface SimCamFakeConnection : AVCaptureConnection
|
|
47
|
+
@end
|
|
48
|
+
@interface SimCamFakeResolvedPhotoSettings : AVCaptureResolvedPhotoSettings
|
|
49
|
+
@end
|
|
50
|
+
|
|
51
|
+
@interface SimCamFakePhoto : AVCapturePhoto
|
|
52
|
+
+ (instancetype)photoFromImage:(CGImageRef)cgImage
|
|
53
|
+
jpegQuality:(CGFloat)q
|
|
54
|
+
mirrored:(BOOL)mirrored;
|
|
55
|
+
@end
|
|
56
|
+
|
|
57
|
+
@interface SimCamAttitude : CMAttitude
|
|
58
|
+
@end
|
|
59
|
+
@interface SimCamAccelerometerData : CMAccelerometerData
|
|
60
|
+
@end
|
|
61
|
+
@interface SimCamGyroData : CMGyroData
|
|
62
|
+
@end
|
|
63
|
+
@interface SimCamMagnetometerData : CMMagnetometerData
|
|
64
|
+
@end
|
|
65
|
+
@interface SimCamDeviceMotion : CMDeviceMotion
|
|
66
|
+
@end
|
|
67
|
+
|
|
68
|
+
#pragma mark - Fake-object factories / caches
|
|
69
|
+
|
|
70
|
+
AVCaptureDeviceFormat *SimCamSharedFakeFormat(void);
|
|
71
|
+
AVCaptureDevice *SimCamFakeDeviceForPosition(AVCaptureDevicePosition p);
|
|
72
|
+
AVCaptureDeviceInput *SimCamFakeInputForPosition(AVCaptureDevicePosition p);
|
|
73
|
+
|
|
74
|
+
AVCaptureConnection *SimCamFakeConnectionForOutput(AVCaptureOutput *out);
|
|
75
|
+
|
|
76
|
+
CMAttitude *SimCamSharedAttitude(void);
|
|
77
|
+
CMAccelerometerData *SimCamSharedAccelerometerData(void);
|
|
78
|
+
CMGyroData *SimCamSharedGyroData(void);
|
|
79
|
+
CMMagnetometerData *SimCamSharedMagnetometerData(void);
|
|
80
|
+
CMDeviceMotion *SimCamSharedDeviceMotion(void);
|
|
81
|
+
|
|
82
|
+
#pragma mark - Fake-input marking (used by AVCaptureDeviceInput swizzle)
|
|
83
|
+
|
|
84
|
+
void SimCamMarkFakeInput(id input, AVCaptureDevice *fakeDevice);
|
|
85
|
+
BOOL SimCamIsFakeInput(id input);
|
|
86
|
+
AVCaptureDevice *SimCamFakeInputDevice(id input);
|