node-mac-recorder 2.22.21 → 2.22.23
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.
|
@@ -105,6 +105,37 @@ function transformInputFrameGlobal(ifr, d) {
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
// Electron: AX caret cache — deferred refresh ile crash-safe
|
|
109
|
+
const ELECTRON_AX_CACHE_MAX_AGE_MS = 500;
|
|
110
|
+
|
|
111
|
+
function deferredRefreshElectronAXCache(recorder, nativeBinding) {
|
|
112
|
+
if (recorder._axDeferredPending) return;
|
|
113
|
+
if (!nativeBinding || typeof nativeBinding.getTextInputSnapshot !== "function") return;
|
|
114
|
+
|
|
115
|
+
recorder._axDeferredPending = true;
|
|
116
|
+
setImmediate(() => {
|
|
117
|
+
recorder._axDeferredPending = false;
|
|
118
|
+
try {
|
|
119
|
+
const snap = nativeBinding.getTextInputSnapshot();
|
|
120
|
+
if (snap && Number.isFinite(snap.caretX) && Number.isFinite(snap.caretY)) {
|
|
121
|
+
const d = recorder.cursorDisplayInfo;
|
|
122
|
+
const caretT = transformGlobalToVideo(snap.caretX, snap.caretY, d);
|
|
123
|
+
recorder._axCaretCache = {
|
|
124
|
+
caretX: caretT.x,
|
|
125
|
+
caretY: caretT.y,
|
|
126
|
+
inputFrame: snap.inputFrame
|
|
127
|
+
? transformInputFrameGlobal(snap.inputFrame, d)
|
|
128
|
+
: null,
|
|
129
|
+
csys: d && d.videoRelative ? "video-relative" : "global",
|
|
130
|
+
wallMs: Date.now(),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
// AX API hata verirse mevcut cache'i koru
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
108
139
|
/** @returns {boolean} Dosyaya textInput satırı yazıldı mı */
|
|
109
140
|
function tryAppendSyntheticTextInputRow(recorder, nativeBinding, filepath, cursorData, timestamp) {
|
|
110
141
|
if (!IS_ELECTRON) {
|
|
@@ -122,6 +153,8 @@ function tryAppendSyntheticTextInputRow(recorder, nativeBinding, filepath, curso
|
|
|
122
153
|
wall - (recorder._electronSynthWallMs || 0) <
|
|
123
154
|
ELECTRON_SYNTH_THROTTLE_MS
|
|
124
155
|
) {
|
|
156
|
+
// Throttle aktif olsa bile AX cache yenilensin
|
|
157
|
+
deferredRefreshElectronAXCache(recorder, nativeBinding);
|
|
125
158
|
return false;
|
|
126
159
|
}
|
|
127
160
|
const vx = cursorData.x;
|
|
@@ -130,36 +163,28 @@ function tryAppendSyntheticTextInputRow(recorder, nativeBinding, filepath, curso
|
|
|
130
163
|
return false;
|
|
131
164
|
}
|
|
132
165
|
|
|
133
|
-
//
|
|
166
|
+
// Cached AX caret varsa gerçek caret pozisyonunu kullan
|
|
134
167
|
let caretX = vx;
|
|
135
168
|
let caretY = vy;
|
|
136
169
|
let inputFrame = { x: vx - 1, y: vy - 9, width: 2, height: 18 };
|
|
137
|
-
let
|
|
170
|
+
let coordinateSystem = cursorData.coordinateSystem;
|
|
138
171
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
caretX = caretT.x;
|
|
151
|
-
caretY = caretT.y;
|
|
152
|
-
hasRealCaret = true;
|
|
153
|
-
|
|
154
|
-
if (snap.inputFrame) {
|
|
155
|
-
inputFrame = transformInputFrameGlobal(snap.inputFrame, d);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
} catch {
|
|
159
|
-
// AX API hata verirse cursor pozisyonuna fallback
|
|
160
|
-
}
|
|
172
|
+
const cache = recorder._axCaretCache;
|
|
173
|
+
if (
|
|
174
|
+
cache &&
|
|
175
|
+
Number.isFinite(cache.caretX) &&
|
|
176
|
+
Number.isFinite(cache.caretY) &&
|
|
177
|
+
(wall - (cache.wallMs || 0)) < ELECTRON_AX_CACHE_MAX_AGE_MS
|
|
178
|
+
) {
|
|
179
|
+
caretX = cache.caretX;
|
|
180
|
+
caretY = cache.caretY;
|
|
181
|
+
if (cache.inputFrame) inputFrame = cache.inputFrame;
|
|
182
|
+
coordinateSystem = cache.csys || coordinateSystem;
|
|
161
183
|
}
|
|
162
184
|
|
|
185
|
+
// Sonraki frame için AX cache'i yenile (deferred — crash-safe)
|
|
186
|
+
deferredRefreshElectronAXCache(recorder, nativeBinding);
|
|
187
|
+
|
|
163
188
|
const tiRow = {
|
|
164
189
|
x: vx,
|
|
165
190
|
y: vy,
|
|
@@ -170,9 +195,7 @@ function tryAppendSyntheticTextInputRow(recorder, nativeBinding, filepath, curso
|
|
|
170
195
|
caretX,
|
|
171
196
|
caretY,
|
|
172
197
|
inputFrame,
|
|
173
|
-
coordinateSystem
|
|
174
|
-
? (recorder.cursorDisplayInfo?.videoRelative ? "video-relative" : "global")
|
|
175
|
-
: cursorData.coordinateSystem,
|
|
198
|
+
coordinateSystem,
|
|
176
199
|
recordingType: cursorData.recordingType,
|
|
177
200
|
videoInfo: cursorData.videoInfo || {},
|
|
178
201
|
displayInfo: cursorData.displayInfo || {},
|
package/package.json
CHANGED
|
@@ -502,7 +502,8 @@ extern "C" bool stopAVFoundationRecording() {
|
|
|
502
502
|
|
|
503
503
|
// Finish writing with null checks
|
|
504
504
|
AVAssetWriterInput *writerInput = g_avVideoInput;
|
|
505
|
-
|
|
505
|
+
AVAssetWriter *writerRef = g_avWriter;
|
|
506
|
+
if (writerInput && writerRef && writerRef.status == AVAssetWriterStatusWriting) {
|
|
506
507
|
[writerInput markAsFinished];
|
|
507
508
|
}
|
|
508
509
|
|
|
@@ -342,17 +342,23 @@ static void FinishWriter(AVAssetWriter *writer, AVAssetWriterInput *input) {
|
|
|
342
342
|
if (!writer) {
|
|
343
343
|
return;
|
|
344
344
|
}
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
|
|
346
|
+
// markAsFinished sadece AVAssetWriterStatusWriting (1) durumunda çağrılabilir
|
|
347
|
+
// Status 0 (Unknown) veya diğer durumlarda crash eder
|
|
348
|
+
if (input && writer.status == AVAssetWriterStatusWriting) {
|
|
347
349
|
[input markAsFinished];
|
|
348
350
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
351
|
+
|
|
352
|
+
if (writer.status == AVAssetWriterStatusWriting) {
|
|
353
|
+
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
354
|
+
[writer finishWritingWithCompletionHandler:^{
|
|
355
|
+
dispatch_semaphore_signal(semaphore);
|
|
356
|
+
}];
|
|
357
|
+
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));
|
|
358
|
+
dispatch_semaphore_wait(semaphore, timeout);
|
|
359
|
+
} else if (writer.status == AVAssetWriterStatusFailed) {
|
|
360
|
+
NSLog(@"⚠️ Writer already failed: %@", writer.error);
|
|
361
|
+
}
|
|
356
362
|
}
|
|
357
363
|
|
|
358
364
|
static void CleanupWriters(void) {
|
|
@@ -373,10 +379,10 @@ static void CleanupWriters(void) {
|
|
|
373
379
|
}
|
|
374
380
|
|
|
375
381
|
if (g_audioWriter) {
|
|
376
|
-
if (g_systemAudioInput) {
|
|
382
|
+
if (g_systemAudioInput && g_audioWriter.status == AVAssetWriterStatusWriting) {
|
|
377
383
|
[g_systemAudioInput markAsFinished];
|
|
378
384
|
}
|
|
379
|
-
if (g_microphoneAudioInput) {
|
|
385
|
+
if (g_microphoneAudioInput && g_audioWriter.status == AVAssetWriterStatusWriting) {
|
|
380
386
|
[g_microphoneAudioInput markAsFinished];
|
|
381
387
|
}
|
|
382
388
|
FinishWriter(g_audioWriter, nil);
|