node-mac-recorder 2.24.9 → 2.24.11
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/MultiWindowRecorder.js +85 -4
- package/README.md +18 -1
- package/index-multiprocess.js +50 -6
- package/index.js +153 -41
- package/package.json +1 -1
- package/recorder-worker.js +159 -69
- package/recorder_runtime_safety.cjs +101 -0
- package/src/audio_recorder.mm +43 -43
- package/src/avfoundation_recorder.mm +103 -69
- package/src/camera_recorder.mm +69 -78
- package/src/ios_device_recorder.mm +121 -20
- package/src/keyboard_tracker.mm +8 -1
- package/src/mac_recorder.mm +84 -13
- package/src/recording_writer_safety.h +66 -0
- package/src/screen_capture_kit.h +4 -2
- package/src/screen_capture_kit.mm +264 -199
- package/src/sync_timeline.h +9 -0
- package/src/sync_timeline.mm +54 -0
package/src/sync_timeline.h
CHANGED
|
@@ -44,6 +44,15 @@ BOOL MRSyncShouldHoldForPrimary(CMTime timestamp);
|
|
|
44
44
|
void MRSyncSetStopLimitSeconds(double seconds);
|
|
45
45
|
double MRSyncGetStopLimitSeconds(void);
|
|
46
46
|
|
|
47
|
+
// Pause/resume the shared media timeline without closing any writers. Samples
|
|
48
|
+
// received while paused are discarded; resumed samples are shifted backwards
|
|
49
|
+
// by the accumulated pause duration so every track remains gap-free.
|
|
50
|
+
void MRSyncPause(void);
|
|
51
|
+
void MRSyncResume(void);
|
|
52
|
+
BOOL MRSyncIsPaused(void);
|
|
53
|
+
CMTime MRSyncAdjustForPauses(CMTime relativeTimestamp);
|
|
54
|
+
double MRSyncGetPausedDurationSeconds(void);
|
|
55
|
+
|
|
47
56
|
#ifdef __cplusplus
|
|
48
57
|
}
|
|
49
58
|
#endif
|
package/src/sync_timeline.mm
CHANGED
|
@@ -17,6 +17,9 @@ static BOOL g_videoHoldLogged = NO;
|
|
|
17
17
|
static CMTime g_audioFirstTimestamp = kCMTimeInvalid;
|
|
18
18
|
static CMTime g_alignmentDelta = kCMTimeInvalid;
|
|
19
19
|
static double g_stopLimitSeconds = -1.0;
|
|
20
|
+
static BOOL g_isPaused = NO;
|
|
21
|
+
static CFAbsoluteTime g_pauseStartedAt = 0;
|
|
22
|
+
static double g_totalPausedSeconds = 0;
|
|
20
23
|
|
|
21
24
|
// Bidirectional barrier: camera side
|
|
22
25
|
static BOOL g_expectCamera = NO;
|
|
@@ -41,6 +44,9 @@ void MRSyncConfigure(BOOL expectAudio) {
|
|
|
41
44
|
g_audioFirstTimestamp = kCMTimeInvalid;
|
|
42
45
|
g_alignmentDelta = kCMTimeInvalid;
|
|
43
46
|
g_stopLimitSeconds = -1.0;
|
|
47
|
+
g_isPaused = NO;
|
|
48
|
+
g_pauseStartedAt = 0;
|
|
49
|
+
g_totalPausedSeconds = 0;
|
|
44
50
|
// Reset camera barrier state
|
|
45
51
|
g_expectCamera = NO;
|
|
46
52
|
g_cameraReady = YES;
|
|
@@ -55,6 +61,54 @@ void MRSyncConfigure(BOOL expectAudio) {
|
|
|
55
61
|
});
|
|
56
62
|
}
|
|
57
63
|
|
|
64
|
+
void MRSyncPause(void) {
|
|
65
|
+
dispatch_sync(MRSyncQueue(), ^{
|
|
66
|
+
if (g_isPaused) return;
|
|
67
|
+
g_isPaused = YES;
|
|
68
|
+
g_pauseStartedAt = CFAbsoluteTimeGetCurrent();
|
|
69
|
+
});
|
|
70
|
+
MRLog(@"⏸️ Recording timeline paused");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void MRSyncResume(void) {
|
|
74
|
+
__block BOOL resumed = NO;
|
|
75
|
+
dispatch_sync(MRSyncQueue(), ^{
|
|
76
|
+
if (!g_isPaused) return;
|
|
77
|
+
if (g_pauseStartedAt > 0) {
|
|
78
|
+
g_totalPausedSeconds += MAX(0, CFAbsoluteTimeGetCurrent() - g_pauseStartedAt);
|
|
79
|
+
}
|
|
80
|
+
g_pauseStartedAt = 0;
|
|
81
|
+
g_isPaused = NO;
|
|
82
|
+
resumed = YES;
|
|
83
|
+
});
|
|
84
|
+
if (resumed) MRLog(@"▶️ Recording timeline resumed");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
BOOL MRSyncIsPaused(void) {
|
|
88
|
+
__block BOOL paused = NO;
|
|
89
|
+
dispatch_sync(MRSyncQueue(), ^{ paused = g_isPaused; });
|
|
90
|
+
return paused;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
double MRSyncGetPausedDurationSeconds(void) {
|
|
94
|
+
__block double duration = 0;
|
|
95
|
+
dispatch_sync(MRSyncQueue(), ^{
|
|
96
|
+
duration = g_totalPausedSeconds;
|
|
97
|
+
if (g_isPaused && g_pauseStartedAt > 0) {
|
|
98
|
+
duration += MAX(0, CFAbsoluteTimeGetCurrent() - g_pauseStartedAt);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return duration;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
CMTime MRSyncAdjustForPauses(CMTime relativeTimestamp) {
|
|
105
|
+
if (!CMTIME_IS_VALID(relativeTimestamp)) return relativeTimestamp;
|
|
106
|
+
double seconds = CMTimeGetSeconds(relativeTimestamp) - MRSyncGetPausedDurationSeconds();
|
|
107
|
+
if (!isfinite(seconds) || seconds <= 0) return kCMTimeZero;
|
|
108
|
+
int32_t timescale = relativeTimestamp.timescale > 0 ? relativeTimestamp.timescale : 600;
|
|
109
|
+
return CMTimeMakeWithSeconds(seconds, timescale);
|
|
110
|
+
}
|
|
111
|
+
|
|
58
112
|
BOOL MRSyncShouldHoldVideoFrame(CMTime timestamp) {
|
|
59
113
|
if (!CMTIME_IS_VALID(timestamp)) {
|
|
60
114
|
return NO;
|