node-mac-recorder 2.24.3 → 2.24.4
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/index.js +85 -37
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -2,6 +2,12 @@ const { EventEmitter } = require("events");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
|
|
5
|
+
// Videonun ilk karesi yakalanana kadar beklenecek ust sinir.
|
|
6
|
+
// Normalde birkac yuz ms; clamshell'de ekran envanteri yeniden kuruldugu icin
|
|
7
|
+
// saniyelere cikabiliyor. Beklemek, cursor'i yanlis referansa baglamaktan iyidir.
|
|
8
|
+
const VIDEO_START_TIMEOUT_MS = 12000;
|
|
9
|
+
const VIDEO_START_POLL_MS = 10;
|
|
10
|
+
|
|
5
11
|
// Auto-switch to Electron-safe implementation when running under Electron and binary exists
|
|
6
12
|
let USE_ELECTRON_SAFE = false;
|
|
7
13
|
let ElectronSafeMacRecorder = null;
|
|
@@ -860,54 +866,89 @@ class MacRecorder extends EventEmitter {
|
|
|
860
866
|
|
|
861
867
|
// Only start cursor if native recording started successfully
|
|
862
868
|
if (success) {
|
|
863
|
-
//
|
|
864
|
-
//
|
|
869
|
+
// ==========================================================
|
|
870
|
+
// VIDEONUN ILK KARESINI BEKLE — SENKRONUN TEMELI
|
|
871
|
+
// ==========================================================
|
|
872
|
+
// getRecordingStatus() BU AMAC ICIN KULLANILAMAZ:
|
|
873
|
+
// ScreenCaptureKit stream'i henuz baslamadiysa native taraf
|
|
874
|
+
// `g_isRecording` fallback'ine dusup TRUE donuyor, yani
|
|
875
|
+
// "kayit komutu verildi"yi "kayit basladi" saniyor.
|
|
876
|
+
// Olculdu: "fully ready after 0ms" + videoStartTimestamp = 0.
|
|
877
|
+
// Sonucu: cursor timeline'i video'dan ONCE basliyor. Normal
|
|
878
|
+
// kosulda fark ~150-300ms, clamshell'de (ekran envanteri
|
|
879
|
+
// yeniden kuruldugu icin) SANIYELER — editordeki 1sn'lik
|
|
880
|
+
// telafi limiti bunu kapatamiyor ve cursor gorunur sekilde kayiyor.
|
|
881
|
+
//
|
|
882
|
+
// Dogru sinyal getVideoStartTimestamp(): native ilk kareyi
|
|
883
|
+
// yakalayip g_videoStartTime'i set ettiginde gecerli bir
|
|
884
|
+
// wall-clock degeri doner. Timeline'i buna baglayinca
|
|
885
|
+
// baslatma hizi (prewarm vb.) senkronu ETKILEMEZ.
|
|
886
|
+
const usesScreenCaptureKit =
|
|
887
|
+
this.options.preferScreenCaptureKit === true;
|
|
888
|
+
|
|
889
|
+
const readVideoStart = () => {
|
|
890
|
+
try {
|
|
891
|
+
const value = Number(
|
|
892
|
+
typeof nativeBinding.getVideoStartTimestamp === 'function'
|
|
893
|
+
? nativeBinding.getVideoStartTimestamp()
|
|
894
|
+
: 0
|
|
895
|
+
);
|
|
896
|
+
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
897
|
+
} catch (_) {
|
|
898
|
+
return 0;
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
let videoStartTimestamp = 0;
|
|
865
903
|
const waitStart = Date.now();
|
|
866
|
-
|
|
904
|
+
|
|
905
|
+
if (usesScreenCaptureKit) {
|
|
906
|
+
while (Date.now() - waitStart < VIDEO_START_TIMEOUT_MS) {
|
|
907
|
+
videoStartTimestamp = readVideoStart();
|
|
908
|
+
if (videoStartTimestamp > 0) {
|
|
909
|
+
console.log(
|
|
910
|
+
`✅ SYNC: Video ilk karesi ${Date.now() - waitStart}ms'de yakalandi`
|
|
911
|
+
);
|
|
912
|
+
break;
|
|
913
|
+
}
|
|
914
|
+
await new Promise(r => setTimeout(r, VIDEO_START_POLL_MS));
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
if (!videoStartTimestamp) {
|
|
918
|
+
console.warn(
|
|
919
|
+
`⚠️ SYNC: Video baslangici ${VIDEO_START_TIMEOUT_MS}ms icinde okunamadi — heuristik hizalamaya dusuluyor`
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
} else {
|
|
923
|
+
// AVFoundation yolunda video-start damgasi yok; eski
|
|
924
|
+
// hazir-olma beklemesi korunuyor.
|
|
867
925
|
while (Date.now() - waitStart < 600) {
|
|
868
926
|
try {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
927
|
+
if (
|
|
928
|
+
nativeBinding &&
|
|
929
|
+
nativeBinding.getRecordingStatus &&
|
|
930
|
+
nativeBinding.getRecordingStatus()
|
|
931
|
+
) {
|
|
872
932
|
break;
|
|
873
933
|
}
|
|
874
934
|
} catch (_) {}
|
|
875
935
|
await new Promise(r => setTimeout(r, 30));
|
|
876
936
|
}
|
|
877
|
-
}
|
|
937
|
+
}
|
|
938
|
+
|
|
878
939
|
this.sessionTimestamp = sessionTimestamp;
|
|
940
|
+
this.videoStartTimestamp = videoStartTimestamp;
|
|
941
|
+
// Onceki kayittan kalan referans sizmasin
|
|
942
|
+
this.timelineStartTimestamp = 0;
|
|
879
943
|
|
|
880
|
-
// Native sync_timeline handles A/V alignment - no JS-level delay needed
|
|
881
944
|
const syncTimestamp = Date.now();
|
|
882
945
|
this.syncTimestamp = syncTimestamp;
|
|
883
946
|
this.recordingStartTime = syncTimestamp;
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
// baslatma gecikmesi + yukaridaki hazir-olma beklemesi). Editor
|
|
890
|
-
// "cursor'un ilk ornegi = video t=0" varsayarsa aradaki fark sabit
|
|
891
|
-
// bir zaman kaymasi olarak kalir. Native gercek video baslangicini
|
|
892
|
-
// biliyor; okuyup sakla ki stop'ta cursor JSON'una yazabilelim.
|
|
893
|
-
this.videoStartTimestamp = 0;
|
|
894
|
-
// Onceki kayittan kalan referans sizmasin
|
|
895
|
-
this.timelineStartTimestamp = 0;
|
|
896
|
-
try {
|
|
897
|
-
const nativeVideoStart =
|
|
898
|
-
typeof nativeBinding.getVideoStartTimestamp === 'function'
|
|
899
|
-
? Number(nativeBinding.getVideoStartTimestamp())
|
|
900
|
-
: 0;
|
|
901
|
-
if (Number.isFinite(nativeVideoStart) && nativeVideoStart > 0) {
|
|
902
|
-
this.videoStartTimestamp = nativeVideoStart;
|
|
903
|
-
console.log(
|
|
904
|
-
`🎯 SYNC: Video first-frame timestamp: ${nativeVideoStart} (cursor starts ${(syncTimestamp - nativeVideoStart).toFixed(0)}ms later)`
|
|
905
|
-
);
|
|
906
|
-
} else {
|
|
907
|
-
console.warn('⚠️ SYNC: Video start timestamp unavailable — cursor sync metadata will be skipped');
|
|
908
|
-
}
|
|
909
|
-
} catch (videoStartError) {
|
|
910
|
-
console.warn('⚠️ SYNC: Video start timestamp read failed:', videoStartError.message);
|
|
947
|
+
|
|
948
|
+
if (videoStartTimestamp > 0) {
|
|
949
|
+
console.log(
|
|
950
|
+
`🎯 SYNC: Video first-frame timestamp: ${videoStartTimestamp} (JS bunu ${(syncTimestamp - videoStartTimestamp).toFixed(0)}ms sonra fark etti)`
|
|
951
|
+
);
|
|
911
952
|
}
|
|
912
953
|
|
|
913
954
|
// ZAMAN REFERANSI: cursor/klavye timeline'i VIDEONUN ilk karesine
|
|
@@ -1174,9 +1215,16 @@ class MacRecorder extends EventEmitter {
|
|
|
1174
1215
|
|
|
1175
1216
|
return new Promise(async (resolve, reject) => {
|
|
1176
1217
|
const stopRequestedAt = Date.now();
|
|
1218
|
+
// Sure, VIDEONUN baslangicindan olculur. recordingStartTime (JS'in
|
|
1219
|
+
// hazir oldugunu fark ettigi an) videodan birkac on ms sonra oldugu
|
|
1220
|
+
// icin buradan hesaplanan stopLimit videoyu kuyrugundan kirpiyordu.
|
|
1221
|
+
const durationReference =
|
|
1222
|
+
this.timelineStartTimestamp && this.timelineStartTimestamp > 0
|
|
1223
|
+
? this.timelineStartTimestamp
|
|
1224
|
+
: this.recordingStartTime;
|
|
1177
1225
|
const elapsedSeconds =
|
|
1178
|
-
|
|
1179
|
-
? (stopRequestedAt -
|
|
1226
|
+
durationReference && durationReference > 0
|
|
1227
|
+
? (stopRequestedAt - durationReference) / 1000
|
|
1180
1228
|
: -1;
|
|
1181
1229
|
try {
|
|
1182
1230
|
console.log('🛑 SYNC: Stopping all recording components simultaneously');
|