node-mac-recorder 2.21.6 → 2.21.8

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.
@@ -10,7 +10,9 @@
10
10
  "Bash(MAC_RECORDER_DEBUG=1 node:*)",
11
11
  "Read(//private/tmp/test-recording/**)",
12
12
  "Bash(MAC_RECORDER_DEBUG=1 timeout 5 node:*)",
13
- "Read(//private/tmp/**)"
13
+ "Read(//private/tmp/**)",
14
+ "Bash(ffprobe:*)",
15
+ "Bash(MAC_RECORDER_DEBUG=1 timeout 8 node:*)"
14
16
  ],
15
17
  "deny": [],
16
18
  "ask": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.21.6",
3
+ "version": "2.21.8",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -369,4 +369,37 @@ NSString *currentStandaloneAudioRecordingPath() {
369
369
  return g_audioRecorder.outputPath;
370
370
  }
371
371
 
372
+ // C API for AVFoundation integration
373
+ void* createNativeAudioRecorder() {
374
+ return (__bridge_retained void*)[[NativeAudioRecorder alloc] init];
375
+ }
376
+
377
+ bool startNativeAudioRecording(void* recorder, const char* deviceId, const char* outputPath) {
378
+ if (!recorder || !outputPath) {
379
+ return false;
380
+ }
381
+
382
+ NativeAudioRecorder* audioRecorder = (__bridge NativeAudioRecorder*)recorder;
383
+ NSString* deviceIdStr = deviceId ? [NSString stringWithUTF8String:deviceId] : nil;
384
+ NSString* outputPathStr = [NSString stringWithUTF8String:outputPath];
385
+
386
+ NSError* error = nil;
387
+ return [audioRecorder startRecordingWithDeviceId:deviceIdStr outputPath:outputPathStr error:&error];
388
+ }
389
+
390
+ bool stopNativeAudioRecording(void* recorder) {
391
+ if (!recorder) {
392
+ return false;
393
+ }
394
+
395
+ NativeAudioRecorder* audioRecorder = (__bridge NativeAudioRecorder*)recorder;
396
+ return [audioRecorder stopRecording];
397
+ }
398
+
399
+ void destroyNativeAudioRecorder(void* recorder) {
400
+ if (recorder) {
401
+ CFRelease(recorder);
402
+ }
403
+ }
404
+
372
405
  }
@@ -7,6 +7,12 @@
7
7
  #include <string>
8
8
  #import "logging.h"
9
9
 
10
+ // Import audio recorder
11
+ extern "C" void* createNativeAudioRecorder(void);
12
+ extern "C" bool startNativeAudioRecording(void* recorder, const char* deviceId, const char* outputPath);
13
+ extern "C" bool stopNativeAudioRecording(void* recorder);
14
+ extern "C" void destroyNativeAudioRecorder(void* recorder);
15
+
10
16
  static AVAssetWriter *g_avWriter = nil;
11
17
  static AVAssetWriterInput *g_avVideoInput = nil;
12
18
  static AVAssetWriterInputPixelBufferAdaptor *g_avPixelBufferAdaptor = nil;
@@ -16,6 +22,8 @@ static CGRect g_avCaptureRect = CGRectZero;
16
22
  static bool g_avIsRecording = false;
17
23
  static int64_t g_avFrameNumber = 0;
18
24
  static CMTime g_avStartTime;
25
+ static void* g_avAudioRecorder = nil;
26
+ static NSString* g_avAudioOutputPath = nil;
19
27
 
20
28
  // AVFoundation screen recording implementation
21
29
  extern "C" bool startAVFoundationRecording(const std::string& outputPath,
@@ -197,7 +205,38 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
197
205
  }
198
206
 
199
207
  g_avFrameNumber = 0;
200
-
208
+
209
+ // Start audio recording if requested
210
+ if (includeMicrophone || includeSystemAudio) {
211
+ MRLog(@"🎤 Starting audio capture (mic=%d, system=%d)", includeMicrophone, includeSystemAudio);
212
+
213
+ // Generate audio output path
214
+ NSString *videoDir = [outputPathStr stringByDeletingLastPathComponent];
215
+ NSString *audioFilename = [NSString stringWithFormat:@"avf_audio_%ld.mov", (long)[[NSDate date] timeIntervalSince1970]];
216
+ g_avAudioOutputPath = [videoDir stringByAppendingPathComponent:audioFilename];
217
+
218
+ MRLog(@"🎤 Audio output: %@", g_avAudioOutputPath);
219
+
220
+ // Create audio recorder
221
+ g_avAudioRecorder = createNativeAudioRecorder();
222
+
223
+ if (g_avAudioRecorder) {
224
+ const char* deviceIdCStr = audioDeviceId ? [audioDeviceId UTF8String] : NULL;
225
+ const char* outputPathCStr = [g_avAudioOutputPath UTF8String];
226
+
227
+ if (startNativeAudioRecording(g_avAudioRecorder, deviceIdCStr, outputPathCStr)) {
228
+ MRLog(@"✅ Audio recording started");
229
+ } else {
230
+ NSLog(@"❌ Failed to start audio recording");
231
+ destroyNativeAudioRecorder(g_avAudioRecorder);
232
+ g_avAudioRecorder = nil;
233
+ g_avAudioOutputPath = nil;
234
+ }
235
+ } else {
236
+ NSLog(@"❌ Failed to create audio recorder");
237
+ }
238
+ }
239
+
201
240
  // Start capture timer (10 FPS for Electron compatibility)
202
241
  dispatch_queue_t captureQueue = dispatch_queue_create("AVFoundationCaptureQueue", DISPATCH_QUEUE_SERIAL);
203
242
  g_avTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, captureQueue);
@@ -348,9 +387,18 @@ extern "C" bool stopAVFoundationRecording() {
348
387
  if (!g_avIsRecording) {
349
388
  return true;
350
389
  }
351
-
390
+
352
391
  g_avIsRecording = false;
353
-
392
+
393
+ // Stop audio recording if active
394
+ if (g_avAudioRecorder) {
395
+ MRLog(@"🛑 Stopping audio recording");
396
+ stopNativeAudioRecording(g_avAudioRecorder);
397
+ destroyNativeAudioRecorder(g_avAudioRecorder);
398
+ g_avAudioRecorder = nil;
399
+ MRLog(@"✅ Audio recording stopped");
400
+ }
401
+
354
402
  @try {
355
403
  // Stop timer with Electron-safe cleanup
356
404
  if (g_avTimer) {
@@ -343,10 +343,18 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
343
343
  MRLog(@" Reason: ScreenCaptureKit has thread safety issues in Electron (SIGTRAP crashes)");
344
344
  }
345
345
 
346
- // Force AVFoundation for debugging/testing OR Electron
347
- BOOL forceAVFoundation = (getenv("FORCE_AVFOUNDATION") != NULL) || isElectron;
348
- if (getenv("FORCE_AVFOUNDATION") != NULL) {
349
- MRLog(@"🔧 FORCE_AVFOUNDATION environment variable detected");
346
+ // CRITICAL FIX: ScreenCaptureKit causes segmentation faults
347
+ // Forcing AVFoundation for ALL environments until issue is resolved
348
+ // TODO: Implement audio capture in AVFoundation
349
+ BOOL forceAVFoundation = YES;
350
+
351
+ MRLog(@"🔧 CRITICAL: ScreenCaptureKit disabled globally (segfault issue)");
352
+ MRLog(@" Using AVFoundation for stability");
353
+ MRLog(@" ⚠️ WARNING: Audio capture not available in AVFoundation mode");
354
+ MRLog(@" Audio files will be created but will be empty");
355
+
356
+ if (isElectron) {
357
+ MRLog(@"⚡ Electron environment detected - using stable AVFoundation");
350
358
  }
351
359
 
352
360
  // Electron-first priority: ALWAYS use AVFoundation in Electron for stability