node-mac-recorder 2.18.1 → 2.18.3
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 +126 -33
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -252,6 +252,12 @@ class MacRecorder extends EventEmitter {
|
|
|
252
252
|
|
|
253
253
|
// Recording için display bilgisini sakla (cursor capture için)
|
|
254
254
|
const targetDisplay = displays.find(d => d.id === targetDisplayId);
|
|
255
|
+
|
|
256
|
+
// CRITICAL FIX: Get actual physical dimensions for proper cursor scaling
|
|
257
|
+
// This fixes cursor coordinate issues on Retina displays during AVFoundation recording
|
|
258
|
+
const cursorPosition = nativeBinding.getCursorPosition();
|
|
259
|
+
const displayScalingInfo = cursorPosition.displayInfo;
|
|
260
|
+
|
|
255
261
|
this.recordingDisplayInfo = {
|
|
256
262
|
displayId: targetDisplayId,
|
|
257
263
|
x: targetDisplay.x,
|
|
@@ -261,6 +267,10 @@ class MacRecorder extends EventEmitter {
|
|
|
261
267
|
// Add scaling information for cursor coordinate transformation
|
|
262
268
|
logicalWidth: parseInt(targetDisplay.resolution.split("x")[0]),
|
|
263
269
|
logicalHeight: parseInt(targetDisplay.resolution.split("x")[1]),
|
|
270
|
+
// CRITICAL: Add physical dimensions for proper cursor scaling
|
|
271
|
+
physicalWidth: displayScalingInfo ? displayScalingInfo.physicalWidth : parseInt(targetDisplay.resolution.split("x")[0]),
|
|
272
|
+
physicalHeight: displayScalingInfo ? displayScalingInfo.physicalHeight : parseInt(targetDisplay.resolution.split("x")[1]),
|
|
273
|
+
scaleFactor: displayScalingInfo ? displayScalingInfo.scaleFactor : 1.0,
|
|
264
274
|
};
|
|
265
275
|
}
|
|
266
276
|
|
|
@@ -298,6 +308,11 @@ class MacRecorder extends EventEmitter {
|
|
|
298
308
|
}
|
|
299
309
|
|
|
300
310
|
if (targetDisplay) {
|
|
311
|
+
// CRITICAL FIX: Get actual physical dimensions for proper cursor scaling
|
|
312
|
+
// This fixes cursor coordinate issues on Retina displays during AVFoundation recording
|
|
313
|
+
const cursorPosition = nativeBinding.getCursorPosition();
|
|
314
|
+
const displayScalingInfo = cursorPosition.displayInfo;
|
|
315
|
+
|
|
301
316
|
this.recordingDisplayInfo = {
|
|
302
317
|
displayId: targetDisplay.id,
|
|
303
318
|
x: targetDisplay.x || 0,
|
|
@@ -307,6 +322,10 @@ class MacRecorder extends EventEmitter {
|
|
|
307
322
|
// Add scaling information for cursor coordinate transformation
|
|
308
323
|
logicalWidth: parseInt(targetDisplay.resolution.split("x")[0]),
|
|
309
324
|
logicalHeight: parseInt(targetDisplay.resolution.split("x")[1]),
|
|
325
|
+
// CRITICAL: Add physical dimensions for proper cursor scaling
|
|
326
|
+
physicalWidth: displayScalingInfo ? displayScalingInfo.physicalWidth : parseInt(targetDisplay.resolution.split("x")[0]),
|
|
327
|
+
physicalHeight: displayScalingInfo ? displayScalingInfo.physicalHeight : parseInt(targetDisplay.resolution.split("x")[1]),
|
|
328
|
+
scaleFactor: displayScalingInfo ? displayScalingInfo.scaleFactor : 1.0,
|
|
310
329
|
};
|
|
311
330
|
}
|
|
312
331
|
} catch (error) {
|
|
@@ -365,10 +384,10 @@ class MacRecorder extends EventEmitter {
|
|
|
365
384
|
this.isRecording = true;
|
|
366
385
|
this.recordingStartTime = Date.now();
|
|
367
386
|
|
|
368
|
-
// Start unified cursor tracking
|
|
369
|
-
//
|
|
387
|
+
// Start unified cursor tracking with video-relative coordinates
|
|
388
|
+
// This ensures cursor positions match exactly with video frames
|
|
370
389
|
const standardCursorOptions = {
|
|
371
|
-
|
|
390
|
+
videoRelative: true,
|
|
372
391
|
displayInfo: this.recordingDisplayInfo,
|
|
373
392
|
recordingType: this.options.windowId ? 'window' :
|
|
374
393
|
this.options.captureArea ? 'area' : 'display',
|
|
@@ -652,12 +671,14 @@ class MacRecorder extends EventEmitter {
|
|
|
652
671
|
}
|
|
653
672
|
|
|
654
673
|
/**
|
|
655
|
-
* Unified cursor capture for all recording types - uses
|
|
674
|
+
* Unified cursor capture for all recording types - uses video-relative coordinates
|
|
656
675
|
* @param {string|number} intervalOrFilepath - Cursor data JSON dosya yolu veya interval
|
|
657
676
|
* @param {Object} options - Cursor capture seçenekleri
|
|
658
|
-
* @param {boolean} options.
|
|
677
|
+
* @param {boolean} options.videoRelative - Use video-relative coordinates (recommended)
|
|
659
678
|
* @param {Object} options.displayInfo - Display information for coordinate transformation
|
|
660
679
|
* @param {string} options.recordingType - Type of recording: 'display', 'window', 'area'
|
|
680
|
+
* @param {Object} options.captureArea - Capture area for area recording coordinate transformation
|
|
681
|
+
* @param {number} options.windowId - Window ID for window recording coordinate transformation
|
|
661
682
|
*/
|
|
662
683
|
async startCursorCapture(intervalOrFilepath = 100, options = {}) {
|
|
663
684
|
let filepath;
|
|
@@ -679,16 +700,46 @@ class MacRecorder extends EventEmitter {
|
|
|
679
700
|
throw new Error("Cursor capture is already running");
|
|
680
701
|
}
|
|
681
702
|
|
|
682
|
-
// Use
|
|
683
|
-
if (options.
|
|
684
|
-
//
|
|
703
|
+
// Use video-relative coordinate system for all recording types
|
|
704
|
+
if (options.videoRelative && options.displayInfo) {
|
|
705
|
+
// Calculate video offset based on recording type
|
|
706
|
+
let videoOffsetX = 0;
|
|
707
|
+
let videoOffsetY = 0;
|
|
708
|
+
let videoWidth = options.displayInfo.width || options.displayInfo.logicalWidth;
|
|
709
|
+
let videoHeight = options.displayInfo.height || options.displayInfo.logicalHeight;
|
|
710
|
+
|
|
711
|
+
if (options.recordingType === 'window' && options.windowId) {
|
|
712
|
+
// For window recording: offset = window position in display
|
|
713
|
+
if (options.captureArea) {
|
|
714
|
+
videoOffsetX = options.captureArea.x;
|
|
715
|
+
videoOffsetY = options.captureArea.y;
|
|
716
|
+
videoWidth = options.captureArea.width;
|
|
717
|
+
videoHeight = options.captureArea.height;
|
|
718
|
+
}
|
|
719
|
+
} else if (options.recordingType === 'area' && options.captureArea) {
|
|
720
|
+
// For area recording: offset = area position in display
|
|
721
|
+
videoOffsetX = options.captureArea.x;
|
|
722
|
+
videoOffsetY = options.captureArea.y;
|
|
723
|
+
videoWidth = options.captureArea.width;
|
|
724
|
+
videoHeight = options.captureArea.height;
|
|
725
|
+
}
|
|
726
|
+
// For display recording: offset remains 0,0
|
|
727
|
+
|
|
685
728
|
this.cursorDisplayInfo = {
|
|
686
729
|
displayId: options.displayInfo.displayId || options.displayInfo.id,
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
730
|
+
displayX: options.displayInfo.x || 0,
|
|
731
|
+
displayY: options.displayInfo.y || 0,
|
|
732
|
+
displayWidth: options.displayInfo.width || options.displayInfo.logicalWidth,
|
|
733
|
+
displayHeight: options.displayInfo.height || options.displayInfo.logicalHeight,
|
|
734
|
+
// CRITICAL: Add physical dimensions for proper cursor scaling
|
|
735
|
+
physicalWidth: options.displayInfo.physicalWidth || options.displayInfo.width || options.displayInfo.logicalWidth,
|
|
736
|
+
physicalHeight: options.displayInfo.physicalHeight || options.displayInfo.height || options.displayInfo.logicalHeight,
|
|
737
|
+
scaleFactor: options.displayInfo.scaleFactor || 1.0,
|
|
738
|
+
videoOffsetX: videoOffsetX,
|
|
739
|
+
videoOffsetY: videoOffsetY,
|
|
740
|
+
videoWidth: videoWidth,
|
|
741
|
+
videoHeight: videoHeight,
|
|
742
|
+
videoRelative: true,
|
|
692
743
|
recordingType: options.recordingType || 'display',
|
|
693
744
|
// Store additional context for debugging
|
|
694
745
|
captureArea: options.captureArea,
|
|
@@ -698,7 +749,19 @@ class MacRecorder extends EventEmitter {
|
|
|
698
749
|
// Fallback: Use recording display info if available
|
|
699
750
|
this.cursorDisplayInfo = {
|
|
700
751
|
...this.recordingDisplayInfo,
|
|
701
|
-
|
|
752
|
+
displayX: this.recordingDisplayInfo.x || 0,
|
|
753
|
+
displayY: this.recordingDisplayInfo.y || 0,
|
|
754
|
+
displayWidth: this.recordingDisplayInfo.width || this.recordingDisplayInfo.logicalWidth,
|
|
755
|
+
displayHeight: this.recordingDisplayInfo.height || this.recordingDisplayInfo.logicalHeight,
|
|
756
|
+
// CRITICAL: Include physical dimensions from recording display info
|
|
757
|
+
physicalWidth: this.recordingDisplayInfo.physicalWidth || this.recordingDisplayInfo.width || this.recordingDisplayInfo.logicalWidth,
|
|
758
|
+
physicalHeight: this.recordingDisplayInfo.physicalHeight || this.recordingDisplayInfo.height || this.recordingDisplayInfo.logicalHeight,
|
|
759
|
+
scaleFactor: this.recordingDisplayInfo.scaleFactor || 1.0,
|
|
760
|
+
videoOffsetX: 0,
|
|
761
|
+
videoOffsetY: 0,
|
|
762
|
+
videoWidth: this.recordingDisplayInfo.width || this.recordingDisplayInfo.logicalWidth,
|
|
763
|
+
videoHeight: this.recordingDisplayInfo.height || this.recordingDisplayInfo.logicalHeight,
|
|
764
|
+
videoRelative: true,
|
|
702
765
|
recordingType: options.recordingType || 'display'
|
|
703
766
|
};
|
|
704
767
|
} else {
|
|
@@ -738,26 +801,43 @@ class MacRecorder extends EventEmitter {
|
|
|
738
801
|
const position = nativeBinding.getCursorPosition();
|
|
739
802
|
const timestamp = Date.now() - this.cursorCaptureStartTime;
|
|
740
803
|
|
|
741
|
-
//
|
|
804
|
+
// Video-relative coordinate transformation for all recording types
|
|
742
805
|
let x = position.x;
|
|
743
806
|
let y = position.y;
|
|
744
807
|
let coordinateSystem = "global";
|
|
745
808
|
|
|
746
|
-
// Apply
|
|
747
|
-
if (this.cursorDisplayInfo && this.cursorDisplayInfo.
|
|
748
|
-
// Transform global → display-relative coordinates
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
//
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
809
|
+
// Apply video-relative transformation for all recording types
|
|
810
|
+
if (this.cursorDisplayInfo && this.cursorDisplayInfo.videoRelative) {
|
|
811
|
+
// Step 1: Transform global → display-relative coordinates
|
|
812
|
+
const displayRelativeX = position.x - this.cursorDisplayInfo.displayX;
|
|
813
|
+
const displayRelativeY = position.y - this.cursorDisplayInfo.displayY;
|
|
814
|
+
|
|
815
|
+
// CRITICAL FIX: Apply physical scaling for AVFoundation recording
|
|
816
|
+
// AVFoundation records at physical resolution while cursor coordinates are logical
|
|
817
|
+
let scaledX = displayRelativeX;
|
|
818
|
+
let scaledY = displayRelativeY;
|
|
819
|
+
|
|
820
|
+
if (this.cursorDisplayInfo.scaleFactor && this.cursorDisplayInfo.scaleFactor > 1.0) {
|
|
821
|
+
// Scale logical cursor coordinates to match physical video dimensions
|
|
822
|
+
scaledX = displayRelativeX * this.cursorDisplayInfo.scaleFactor;
|
|
823
|
+
scaledY = displayRelativeY * this.cursorDisplayInfo.scaleFactor;
|
|
824
|
+
coordinateSystem = "video-relative-scaled";
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// Step 2: Transform display-relative → video-relative coordinates
|
|
828
|
+
x = scaledX - this.cursorDisplayInfo.videoOffsetX;
|
|
829
|
+
y = scaledY - this.cursorDisplayInfo.videoOffsetY;
|
|
830
|
+
coordinateSystem = this.cursorDisplayInfo.scaleFactor > 1.0 ? "video-relative-scaled" : "video-relative";
|
|
831
|
+
|
|
832
|
+
// Bounds check for video area (use physical video dimensions for AVFoundation)
|
|
833
|
+
const videoWidth = this.cursorDisplayInfo.physicalWidth || this.cursorDisplayInfo.videoWidth;
|
|
834
|
+
const videoHeight = this.cursorDisplayInfo.physicalHeight || this.cursorDisplayInfo.videoHeight;
|
|
835
|
+
|
|
836
|
+
const outsideVideo = x < 0 || y < 0 || x >= videoWidth || y >= videoHeight;
|
|
837
|
+
|
|
838
|
+
// For debugging - add metadata if cursor is outside video area
|
|
839
|
+
if (outsideVideo) {
|
|
840
|
+
coordinateSystem = coordinateSystem + "-outside";
|
|
761
841
|
}
|
|
762
842
|
}
|
|
763
843
|
|
|
@@ -769,12 +849,25 @@ class MacRecorder extends EventEmitter {
|
|
|
769
849
|
cursorType: position.cursorType,
|
|
770
850
|
type: position.eventType || "move",
|
|
771
851
|
coordinateSystem: coordinateSystem,
|
|
772
|
-
//
|
|
852
|
+
// Video-relative metadata for all recording types
|
|
773
853
|
recordingType: this.cursorDisplayInfo?.recordingType || "display",
|
|
854
|
+
videoInfo: this.cursorDisplayInfo ? {
|
|
855
|
+
width: this.cursorDisplayInfo.videoWidth,
|
|
856
|
+
height: this.cursorDisplayInfo.videoHeight,
|
|
857
|
+
offsetX: this.cursorDisplayInfo.videoOffsetX,
|
|
858
|
+
offsetY: this.cursorDisplayInfo.videoOffsetY,
|
|
859
|
+
// CRITICAL: Add physical dimensions for debugging
|
|
860
|
+
physicalWidth: this.cursorDisplayInfo.physicalWidth,
|
|
861
|
+
physicalHeight: this.cursorDisplayInfo.physicalHeight
|
|
862
|
+
} : null,
|
|
774
863
|
displayInfo: this.cursorDisplayInfo ? {
|
|
775
864
|
displayId: this.cursorDisplayInfo.displayId,
|
|
776
|
-
width: this.cursorDisplayInfo.
|
|
777
|
-
height: this.cursorDisplayInfo.
|
|
865
|
+
width: this.cursorDisplayInfo.displayWidth,
|
|
866
|
+
height: this.cursorDisplayInfo.displayHeight,
|
|
867
|
+
// CRITICAL: Add scaling info for debugging AVFoundation cursor issues
|
|
868
|
+
physicalWidth: this.cursorDisplayInfo.physicalWidth,
|
|
869
|
+
physicalHeight: this.cursorDisplayInfo.physicalHeight,
|
|
870
|
+
scaleFactor: this.cursorDisplayInfo.scaleFactor
|
|
778
871
|
} : null
|
|
779
872
|
};
|
|
780
873
|
|