node-mac-recorder 2.21.19 → 2.21.21

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.
@@ -5,7 +5,11 @@
5
5
  "Bash(chmod:*)",
6
6
  "Bash(node test-sync.js:*)",
7
7
  "Bash(node:*)",
8
- "Bash(ALLOW_CONTINUITY_CAMERA=1 node:*)"
8
+ "Bash(ALLOW_CONTINUITY_CAMERA=1 node:*)",
9
+ "Bash(awk:*)",
10
+ "Bash(ffprobe:*)",
11
+ "Bash(sw_vers:*)",
12
+ "Bash(system_profiler:*)"
9
13
  ],
10
14
  "deny": [],
11
15
  "ask": []
package/index.js CHANGED
@@ -449,8 +449,8 @@ class MacRecorder extends EventEmitter {
449
449
  const originalBaseName = path.basename(outputPath, path.extname(outputPath));
450
450
  const extension = path.extname(outputPath);
451
451
 
452
- // Remove any existing timestamp from filename (pattern: -1234567890)
453
- const cleanBaseName = originalBaseName.replace(/-\d{13}$/, '');
452
+ // Remove any existing timestamp from filename (pattern: -1234567890 or _1234567890)
453
+ const cleanBaseName = originalBaseName.replace(/[-_]\d{13}$/, '');
454
454
 
455
455
  // Reconstruct path with sessionTimestamp
456
456
  outputPath = path.join(outputDir, `${cleanBaseName}-${sessionTimestamp}${extension}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.21.19",
3
+ "version": "2.21.21",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -120,16 +120,30 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
120
120
  codecKey = AVVideoCodecH264;
121
121
  }
122
122
 
123
+ // QUALITY FIX: ULTRA HIGH quality screen recording
124
+ // ProMotion displays may capture at 10 FPS - use very high bitrate for perfect quality
125
+ NSInteger bitrate = (NSInteger)(recordingSize.width * recordingSize.height * 30);
126
+ bitrate = MAX(bitrate, 30 * 1000 * 1000); // Minimum 30 Mbps
127
+ bitrate = MIN(bitrate, 120 * 1000 * 1000); // Maximum 120 Mbps
128
+
129
+ NSLog(@"🎬 ULTRA QUALITY AVFoundation: %dx%d, bitrate=%.2fMbps",
130
+ (int)recordingSize.width, (int)recordingSize.height, bitrate / (1000.0 * 1000.0));
131
+
123
132
  NSDictionary *videoSettings = @{
124
133
  AVVideoCodecKey: codecKey,
125
134
  AVVideoWidthKey: @((int)recordingSize.width),
126
135
  AVVideoHeightKey: @((int)recordingSize.height),
127
136
  AVVideoCompressionPropertiesKey: @{
128
- AVVideoAverageBitRateKey: @(recordingSize.width * recordingSize.height * 8),
129
- AVVideoMaxKeyFrameIntervalKey: @30
137
+ AVVideoAverageBitRateKey: @(bitrate),
138
+ AVVideoMaxKeyFrameIntervalKey: @30,
139
+ AVVideoAllowFrameReorderingKey: @YES,
140
+ AVVideoExpectedSourceFrameRateKey: @60,
141
+ AVVideoQualityKey: @(0.95), // 0.0-1.0, higher is better
142
+ AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
143
+ AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC
130
144
  }
131
145
  };
132
-
146
+
133
147
  NSLog(@"🔧 Using codec: %@", codecKey);
134
148
 
135
149
  // Create video input
@@ -33,6 +33,10 @@ static BOOL g_audioWriterStarted = NO;
33
33
  static NSInteger g_configuredSampleRate = 48000;
34
34
  static NSInteger g_configuredChannelCount = 2;
35
35
 
36
+ // Frame rate debugging
37
+ static NSInteger g_frameCount = 0;
38
+ static CFAbsoluteTime g_firstFrameTime = 0;
39
+
36
40
  static void CleanupWriters(void);
37
41
  static AVAssetWriterInputPixelBufferAdaptor * _Nullable CurrentPixelBufferAdaptor(void) {
38
42
  if (!g_pixelBufferAdaptorRef) {
@@ -90,6 +94,10 @@ static void CleanupWriters(void) {
90
94
  }
91
95
  g_videoWriterStarted = NO;
92
96
  g_videoStartTime = kCMTimeInvalid;
97
+
98
+ // Reset frame counting
99
+ g_frameCount = 0;
100
+ g_firstFrameTime = 0;
93
101
  }
94
102
 
95
103
  if (g_audioWriter) {
@@ -217,6 +225,17 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
217
225
  if (!appended) {
218
226
  NSLog(@"⚠️ Failed appending pixel buffer: %@", g_videoWriter.error);
219
227
  }
228
+
229
+ // Frame rate debugging
230
+ g_frameCount++;
231
+ if (g_firstFrameTime == 0) {
232
+ g_firstFrameTime = CFAbsoluteTimeGetCurrent();
233
+ }
234
+ if (g_frameCount % 60 == 0) {
235
+ CFAbsoluteTime elapsed = CFAbsoluteTimeGetCurrent() - g_firstFrameTime;
236
+ double actualFPS = g_frameCount / elapsed;
237
+ MRLog(@"📊 Frame stats: %ld frames in %.1fs = %.1f FPS", (long)g_frameCount, elapsed, actualFPS);
238
+ }
220
239
  }
221
240
  @end
222
241
 
@@ -310,11 +329,27 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
310
329
  return NO;
311
330
  }
312
331
 
332
+ // QUALITY FIX: ULTRA HIGH quality for screen recording
333
+ // ProMotion displays may run at 10Hz (low power) = 10 FPS capture
334
+ // Solution: Use VERY HIGH bitrate so each frame is perfect quality
335
+ // Use 30x multiplier for ULTRA quality (was 6x - way too low!)
336
+ NSInteger bitrate = (NSInteger)(width * height * 30);
337
+ bitrate = MAX(bitrate, 30 * 1000 * 1000); // Minimum 30 Mbps for crystal clear screen recording
338
+ bitrate = MIN(bitrate, 120 * 1000 * 1000); // Maximum 120 Mbps for ultra quality
339
+
340
+ MRLog(@"🎬 ULTRA QUALITY Screen encoder: %ldx%ld, bitrate=%.2fMbps",
341
+ (long)width, (long)height, bitrate / (1000.0 * 1000.0));
342
+
313
343
  NSDictionary *compressionProps = @{
314
- AVVideoAverageBitRateKey: @(width * height * 6),
315
- AVVideoMaxKeyFrameIntervalKey: @30
344
+ AVVideoAverageBitRateKey: @(bitrate),
345
+ AVVideoMaxKeyFrameIntervalKey: @30,
346
+ AVVideoAllowFrameReorderingKey: @YES,
347
+ AVVideoExpectedSourceFrameRateKey: @60,
348
+ AVVideoQualityKey: @(0.95), // 0.0-1.0, higher is better (0.95 = excellent)
349
+ AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
350
+ AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC
316
351
  };
317
-
352
+
318
353
  NSDictionary *videoSettings = @{
319
354
  AVVideoCodecKey: AVVideoCodecTypeH264,
320
355
  AVVideoWidthKey: @(width),
@@ -602,13 +637,20 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
602
637
  }
603
638
  }
604
639
 
605
- // Configure stream with extracted options
640
+ // Configure stream with HIGH QUALITY settings
606
641
  SCStreamConfiguration *streamConfig = [[SCStreamConfiguration alloc] init];
607
642
  streamConfig.width = recordingWidth;
608
643
  streamConfig.height = recordingHeight;
609
- streamConfig.minimumFrameInterval = CMTimeMake(1, 30); // 30 FPS
644
+ streamConfig.minimumFrameInterval = CMTimeMake(1, 60); // 60 FPS for smooth recording
610
645
  streamConfig.pixelFormat = kCVPixelFormatType_32BGRA;
611
646
  streamConfig.scalesToFit = NO;
647
+
648
+ // QUALITY FIX: Set high quality encoding parameters
649
+ if (@available(macOS 13.0, *)) {
650
+ streamConfig.queueDepth = 8; // Larger queue for smoother capture
651
+ }
652
+
653
+ MRLog(@"🎬 ScreenCaptureKit config: %ldx%ld @ 60fps", (long)recordingWidth, (long)recordingHeight);
612
654
 
613
655
  BOOL shouldCaptureMic = includeMicrophone ? [includeMicrophone boolValue] : NO;
614
656
  BOOL shouldCaptureSystemAudio = includeSystemAudio ? [includeSystemAudio boolValue] : NO;