node-mac-recorder 2.12.4 → 2.12.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.12.4",
3
+ "version": "2.12.5",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -9,6 +9,8 @@ static AVAssetWriter *g_assetWriter = nil;
9
9
  static AVAssetWriterInput *g_videoWriterInput = nil;
10
10
  static AVAssetWriterInput *g_audioWriterInput = nil;
11
11
  static NSString *g_outputPath = nil;
12
+ static BOOL g_sessionStarted = NO;
13
+ static AVAssetWriterInputPixelBufferAdaptor *g_pixelBufferAdaptor = nil;
12
14
 
13
15
  @interface ScreenCaptureKitRecorderDelegate : NSObject <SCStreamDelegate>
14
16
  @property (nonatomic, copy) void (^completionHandler)(NSURL *outputURL, NSError *error);
@@ -36,25 +38,51 @@ static NSString *g_outputPath = nil;
36
38
 
37
39
  @implementation ScreenCaptureKitStreamOutput
38
40
  - (void)stream:(SCStream *)stream didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer ofType:(SCStreamOutputType)type {
39
- if (!g_assetWriter || g_assetWriter.status != AVAssetWriterStatusWriting) {
41
+ if (!g_assetWriter) {
42
+ return;
43
+ }
44
+
45
+ // Start session on first sample
46
+ if (!g_sessionStarted && g_assetWriter.status == AVAssetWriterStatusWriting) {
47
+ CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
48
+ [g_assetWriter startSessionAtSourceTime:presentationTime];
49
+ g_sessionStarted = YES;
50
+ NSLog(@"📽️ ScreenCaptureKit video session started at time: %lld/%d", presentationTime.value, presentationTime.timescale);
51
+ }
52
+
53
+ if (g_assetWriter.status != AVAssetWriterStatusWriting) {
40
54
  return;
41
55
  }
42
56
 
43
57
  switch (type) {
44
58
  case SCStreamOutputTypeScreen:
45
- if (g_videoWriterInput && g_videoWriterInput.isReadyForMoreMediaData) {
46
- [g_videoWriterInput appendSampleBuffer:sampleBuffer];
59
+ if (g_pixelBufferAdaptor && g_pixelBufferAdaptor.assetWriterInput.isReadyForMoreMediaData) {
60
+ // Convert sample buffer to pixel buffer
61
+ CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
62
+ if (pixelBuffer) {
63
+ CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
64
+ BOOL success = [g_pixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:presentationTime];
65
+ if (!success) {
66
+ NSLog(@"❌ Failed to append pixel buffer: %@", g_assetWriter.error);
67
+ }
68
+ }
47
69
  }
48
70
  break;
49
71
  case SCStreamOutputTypeAudio:
50
72
  if (g_audioWriterInput && g_audioWriterInput.isReadyForMoreMediaData) {
51
- [g_audioWriterInput appendSampleBuffer:sampleBuffer];
73
+ BOOL success = [g_audioWriterInput appendSampleBuffer:sampleBuffer];
74
+ if (!success) {
75
+ NSLog(@"❌ Failed to append audio sample: %@", g_assetWriter.error);
76
+ }
52
77
  }
53
78
  break;
54
79
  case SCStreamOutputTypeMicrophone:
55
80
  // Handle microphone input (if needed in future)
56
81
  if (g_audioWriterInput && g_audioWriterInput.isReadyForMoreMediaData) {
57
- [g_audioWriterInput appendSampleBuffer:sampleBuffer];
82
+ BOOL success = [g_audioWriterInput appendSampleBuffer:sampleBuffer];
83
+ if (!success) {
84
+ NSLog(@"❌ Failed to append microphone sample: %@", g_assetWriter.error);
85
+ }
58
86
  }
59
87
  break;
60
88
  }
@@ -322,7 +350,9 @@ static NSString *g_outputPath = nil;
322
350
  g_assetWriter = nil;
323
351
  g_videoWriterInput = nil;
324
352
  g_audioWriterInput = nil;
353
+ g_pixelBufferAdaptor = nil;
325
354
  g_outputPath = nil;
355
+ g_sessionStarted = NO;
326
356
  }];
327
357
  }
328
358
 
@@ -344,25 +374,21 @@ static NSString *g_outputPath = nil;
344
374
  outputPath:(NSString *)outputPath
345
375
  includeAudio:(BOOL)includeAudio {
346
376
 
347
- // Create asset writer
377
+ // Create asset writer with MP4 format for better compatibility
348
378
  NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
349
379
  NSError *error = nil;
350
- g_assetWriter = [[AVAssetWriter alloc] initWithURL:outputURL fileType:AVFileTypeQuickTimeMovie error:&error];
380
+ g_assetWriter = [[AVAssetWriter alloc] initWithURL:outputURL fileType:AVFileTypeMPEG4 error:&error];
351
381
 
352
382
  if (error || !g_assetWriter) {
353
383
  NSLog(@"❌ Failed to create asset writer: %@", error);
354
384
  return NO;
355
385
  }
356
386
 
357
- // Video writer input
387
+ // Video writer input - basic settings for maximum compatibility
358
388
  NSDictionary *videoSettings = @{
359
389
  AVVideoCodecKey: AVVideoCodecTypeH264,
360
390
  AVVideoWidthKey: @(width),
361
- AVVideoHeightKey: @(height),
362
- AVVideoCompressionPropertiesKey: @{
363
- AVVideoAverageBitRateKey: @(width * height * 8), // 8 bits per pixel
364
- AVVideoExpectedSourceFrameRateKey: @(60)
365
- }
391
+ AVVideoHeightKey: @(height)
366
392
  };
367
393
 
368
394
  g_videoWriterInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
@@ -374,6 +400,23 @@ static NSString *g_outputPath = nil;
374
400
  }
375
401
  [g_assetWriter addInput:g_videoWriterInput];
376
402
 
403
+ // Create pixel buffer adaptor for ScreenCaptureKit compatibility
404
+ NSDictionary *pixelBufferAttributes = @{
405
+ (NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
406
+ (NSString *)kCVPixelBufferWidthKey: @(width),
407
+ (NSString *)kCVPixelBufferHeightKey: @(height),
408
+ (NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{}
409
+ };
410
+
411
+ g_pixelBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc]
412
+ initWithAssetWriterInput:g_videoWriterInput
413
+ sourcePixelBufferAttributes:pixelBufferAttributes];
414
+
415
+ if (!g_pixelBufferAdaptor) {
416
+ NSLog(@"❌ Cannot create pixel buffer adaptor");
417
+ return NO;
418
+ }
419
+
377
420
  // Audio writer input (if needed)
378
421
  if (includeAudio) {
379
422
  NSDictionary *audioSettings = @{
@@ -391,13 +434,13 @@ static NSString *g_outputPath = nil;
391
434
  }
392
435
  }
393
436
 
394
- // Start writing
437
+ // Start writing (session will be started when first sample arrives)
395
438
  if (![g_assetWriter startWriting]) {
396
439
  NSLog(@"❌ Failed to start writing: %@", g_assetWriter.error);
397
440
  return NO;
398
441
  }
399
442
 
400
- [g_assetWriter startSessionAtSourceTime:kCMTimeZero];
443
+ g_sessionStarted = NO; // Reset session flag
401
444
  NSLog(@"✅ ScreenCaptureKit video writer setup complete: %@", outputPath);
402
445
 
403
446
  return YES;