node-mac-recorder 2.21.5 → 2.21.7

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.
@@ -8,7 +8,10 @@
8
8
  "Read(//Users/onur/codes/**)",
9
9
  "Bash(log show:*)",
10
10
  "Bash(MAC_RECORDER_DEBUG=1 node:*)",
11
- "Read(//private/tmp/test-recording/**)"
11
+ "Read(//private/tmp/test-recording/**)",
12
+ "Bash(MAC_RECORDER_DEBUG=1 timeout 5 node:*)",
13
+ "Read(//private/tmp/**)",
14
+ "Bash(ffprobe:*)"
12
15
  ],
13
16
  "deny": [],
14
17
  "ask": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.21.5",
3
+ "version": "2.21.7",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -349,6 +349,14 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
349
349
  MRLog(@"🔧 FORCE_AVFOUNDATION environment variable detected");
350
350
  }
351
351
 
352
+ // CRITICAL: ScreenCaptureKit causes segmentation faults in both Node.js and Electron
353
+ // FORCE AVFoundation for ALL environments until Apple fixes stability issues
354
+ forceAVFoundation = YES;
355
+ if (forceAVFoundation) {
356
+ MRLog(@"🔧 CRITICAL: ScreenCaptureKit disabled due to segmentation faults");
357
+ MRLog(@" Using AVFoundation for stability in ALL environments");
358
+ }
359
+
352
360
  // Electron-first priority: ALWAYS use AVFoundation in Electron for stability
353
361
  // ScreenCaptureKit has severe thread safety issues in Electron causing SIGTRAP crashes
354
362
  if (isM15Plus && !forceAVFoundation) {
@@ -225,10 +225,15 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
225
225
 
226
226
  @implementation ScreenCaptureAudioOutput
227
227
  - (void)stream:(SCStream *)stream didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer ofType:(SCStreamOutputType)type API_AVAILABLE(macos(12.3)) {
228
+ static dispatch_once_t onceToken;
229
+ dispatch_once(&onceToken, ^{
230
+ MRLog(@"🎤 First audio sample callback received from ScreenCaptureKit");
231
+ });
232
+
228
233
  if (!g_isRecording || !g_shouldCaptureAudio) {
229
234
  return;
230
235
  }
231
-
236
+
232
237
  if (@available(macOS 13.0, *)) {
233
238
  if (type != SCStreamOutputTypeAudio) {
234
239
  return;
@@ -236,8 +241,9 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
236
241
  } else {
237
242
  return;
238
243
  }
239
-
244
+
240
245
  if (!CMSampleBufferDataIsReady(sampleBuffer)) {
246
+ MRLog(@"⚠️ Audio sample buffer data not ready");
241
247
  return;
242
248
  }
243
249
 
@@ -263,11 +269,21 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
263
269
  }
264
270
 
265
271
  if (!g_audioInput.readyForMoreMediaData) {
272
+ static int notReadyCount = 0;
273
+ if (notReadyCount++ % 100 == 0) {
274
+ MRLog(@"⚠️ Audio input not ready for data (count: %d)", notReadyCount);
275
+ }
266
276
  return;
267
277
  }
268
-
269
- if (![g_audioInput appendSampleBuffer:sampleBuffer]) {
278
+
279
+ BOOL success = [g_audioInput appendSampleBuffer:sampleBuffer];
280
+ if (!success) {
270
281
  NSLog(@"⚠️ Failed appending audio sample buffer: %@", g_audioWriter.error);
282
+ } else {
283
+ static int appendCount = 0;
284
+ if (appendCount++ % 100 == 0) {
285
+ MRLog(@"✅ Audio sample appended successfully (count: %d)", appendCount);
286
+ }
271
287
  }
272
288
  }
273
289
  @end
@@ -365,13 +381,20 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
365
381
  [[NSFileManager defaultManager] removeItemAtURL:audioURL error:nil];
366
382
 
367
383
  NSError *writerError = nil;
384
+ // CRITICAL FIX: AVAssetWriter does NOT support WebM for audio
385
+ // Always use QuickTime Movie format (.mov) for audio files
368
386
  AVFileType requestedFileType = AVFileTypeQuickTimeMovie;
369
- BOOL requestedWebM = NO;
370
- if (@available(macOS 15.0, *)) {
371
- requestedFileType = @"public.webm";
372
- requestedWebM = YES;
387
+
388
+ // Ensure path has .mov extension for audio
389
+ NSString *audioPath = originalPath;
390
+ if (![audioPath.pathExtension.lowercaseString isEqualToString:@"mov"]) {
391
+ MRLog(@"⚠️ Audio path has wrong extension '%@', changing to .mov", audioPath.pathExtension);
392
+ audioPath = [[audioPath stringByDeletingPathExtension] stringByAppendingPathExtension:@"mov"];
393
+ g_audioOutputPath = audioPath;
373
394
  }
374
-
395
+ audioURL = [NSURL fileURLWithPath:audioPath];
396
+ [[NSFileManager defaultManager] removeItemAtURL:audioURL error:nil];
397
+
375
398
  @try {
376
399
  g_audioWriter = [[AVAssetWriter alloc] initWithURL:audioURL fileType:requestedFileType error:&writerError];
377
400
  } @catch (NSException *exception) {
@@ -382,28 +405,6 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
382
405
  g_audioWriter = nil;
383
406
  }
384
407
 
385
- if ((!g_audioWriter || writerError) && requestedWebM) {
386
- MRLog(@"⚠️ ScreenCaptureKit audio writer unavailable (%@) – falling back to QuickTime container", writerError.localizedDescription);
387
- NSString *fallbackPath = [[originalPath stringByDeletingPathExtension] stringByAppendingPathExtension:@"mov"];
388
- if (!fallbackPath || [fallbackPath length] == 0) {
389
- fallbackPath = [originalPath stringByAppendingString:@".mov"];
390
- }
391
- [[NSFileManager defaultManager] removeItemAtPath:fallbackPath error:nil];
392
- NSURL *fallbackURL = [NSURL fileURLWithPath:fallbackPath];
393
- g_audioOutputPath = fallbackPath;
394
- writerError = nil;
395
- @try {
396
- g_audioWriter = [[AVAssetWriter alloc] initWithURL:fallbackURL fileType:AVFileTypeQuickTimeMovie error:&writerError];
397
- } @catch (NSException *exception) {
398
- NSDictionary *info = @{
399
- NSLocalizedDescriptionKey: exception.reason ?: @"Failed to initialize audio writer"
400
- };
401
- writerError = [NSError errorWithDomain:@"ScreenCaptureKitRecorder" code:-202 userInfo:info];
402
- g_audioWriter = nil;
403
- }
404
- audioURL = fallbackURL;
405
- }
406
-
407
408
  if (!g_audioWriter || writerError) {
408
409
  NSLog(@"❌ Failed to create audio writer: %@", writerError);
409
410
  return NO;
@@ -433,7 +434,10 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
433
434
 
434
435
  g_audioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioSettings];
435
436
  g_audioInput.expectsMediaDataInRealTime = YES;
436
-
437
+
438
+ MRLog(@"🎙️ Audio input created: sampleRate=%ld, channels=%ld, bitrate=192k",
439
+ (long)g_configuredSampleRate, (long)channelCount);
440
+
437
441
  if (![g_audioWriter canAddInput:g_audioInput]) {
438
442
  NSLog(@"❌ Audio writer cannot add input");
439
443
  return NO;
@@ -441,7 +445,8 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
441
445
  [g_audioWriter addInput:g_audioInput];
442
446
  g_audioWriterStarted = NO;
443
447
  g_audioStartTime = kCMTimeInvalid;
444
-
448
+
449
+ MRLog(@"✅ Audio writer prepared successfully (path: %@)", g_audioOutputPath);
445
450
  return YES;
446
451
  }
447
452
 
@@ -623,17 +628,28 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
623
628
  }
624
629
 
625
630
  if (@available(macos 13.0, *)) {
631
+ // capturesAudio enables audio capture (both mic and system audio)
626
632
  streamConfig.capturesAudio = g_shouldCaptureAudio;
627
- streamConfig.sampleRate = g_configuredSampleRate;
628
- streamConfig.channelCount = g_configuredChannelCount;
633
+ streamConfig.sampleRate = g_configuredSampleRate ?: 48000;
634
+ streamConfig.channelCount = g_configuredChannelCount ?: 2;
635
+
636
+ // excludesCurrentProcessAudio = YES means ONLY microphone
637
+ // excludesCurrentProcessAudio = NO means system audio + mic
629
638
  streamConfig.excludesCurrentProcessAudio = !shouldCaptureSystemAudio;
639
+
640
+ MRLog(@"🎤 Audio config (macOS 13+): capturesAudio=%d, excludeProcess=%d (mic=%d sys=%d)",
641
+ g_shouldCaptureAudio, streamConfig.excludesCurrentProcessAudio,
642
+ shouldCaptureMic, shouldCaptureSystemAudio);
630
643
  }
631
-
644
+
632
645
  if (@available(macos 15.0, *)) {
646
+ // macOS 15+ has explicit microphone control
633
647
  streamConfig.captureMicrophone = shouldCaptureMic;
634
648
  if (microphoneDeviceId && microphoneDeviceId.length > 0) {
635
649
  streamConfig.microphoneCaptureDeviceID = microphoneDeviceId;
636
650
  }
651
+ MRLog(@"🎤 Microphone (macOS 15+): enabled=%d, deviceID=%@",
652
+ shouldCaptureMic, microphoneDeviceId ?: @"default");
637
653
  }
638
654
 
639
655
  // Apply crop area using sourceRect - CONVERT GLOBAL TO DISPLAY-RELATIVE COORDINATES