node-mac-recorder 2.12.3 → 2.12.4

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.3",
3
+ "version": "2.12.4",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -11,5 +11,9 @@ API_AVAILABLE(macos(12.3))
11
11
  error:(NSError **)error;
12
12
  + (void)stopRecording;
13
13
  + (BOOL)isRecording;
14
+ + (BOOL)setupVideoWriterWithWidth:(NSInteger)width
15
+ height:(NSInteger)height
16
+ outputPath:(NSString *)outputPath
17
+ includeAudio:(BOOL)includeAudio;
14
18
 
15
19
  @end
@@ -3,21 +3,69 @@
3
3
  // Global state
4
4
  static SCStream *g_stream = nil;
5
5
  static id<SCStreamDelegate> g_streamDelegate = nil;
6
+ static id<SCStreamOutput> g_streamOutput = nil;
6
7
  static BOOL g_isRecording = NO;
8
+ static AVAssetWriter *g_assetWriter = nil;
9
+ static AVAssetWriterInput *g_videoWriterInput = nil;
10
+ static AVAssetWriterInput *g_audioWriterInput = nil;
11
+ static NSString *g_outputPath = nil;
7
12
 
8
13
  @interface ScreenCaptureKitRecorderDelegate : NSObject <SCStreamDelegate>
9
14
  @property (nonatomic, copy) void (^completionHandler)(NSURL *outputURL, NSError *error);
10
15
  @end
11
16
 
17
+ @interface ScreenCaptureKitStreamOutput : NSObject <SCStreamOutput>
18
+ @end
19
+
12
20
  @implementation ScreenCaptureKitRecorderDelegate
13
21
  - (void)stream:(SCStream *)stream didStopWithError:(NSError *)error {
14
22
  NSLog(@"ScreenCaptureKit recording stopped with error: %@", error);
23
+
24
+ // Finalize video file
25
+ if (g_assetWriter && g_assetWriter.status == AVAssetWriterStatusWriting) {
26
+ [g_videoWriterInput markAsFinished];
27
+ if (g_audioWriterInput) {
28
+ [g_audioWriterInput markAsFinished];
29
+ }
30
+ [g_assetWriter finishWritingWithCompletionHandler:^{
31
+ NSLog(@"✅ ScreenCaptureKit video file finalized: %@", g_outputPath);
32
+ }];
33
+ }
34
+ }
35
+ @end
36
+
37
+ @implementation ScreenCaptureKitStreamOutput
38
+ - (void)stream:(SCStream *)stream didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer ofType:(SCStreamOutputType)type {
39
+ if (!g_assetWriter || g_assetWriter.status != AVAssetWriterStatusWriting) {
40
+ return;
41
+ }
42
+
43
+ switch (type) {
44
+ case SCStreamOutputTypeScreen:
45
+ if (g_videoWriterInput && g_videoWriterInput.isReadyForMoreMediaData) {
46
+ [g_videoWriterInput appendSampleBuffer:sampleBuffer];
47
+ }
48
+ break;
49
+ case SCStreamOutputTypeAudio:
50
+ if (g_audioWriterInput && g_audioWriterInput.isReadyForMoreMediaData) {
51
+ [g_audioWriterInput appendSampleBuffer:sampleBuffer];
52
+ }
53
+ break;
54
+ case SCStreamOutputTypeMicrophone:
55
+ // Handle microphone input (if needed in future)
56
+ if (g_audioWriterInput && g_audioWriterInput.isReadyForMoreMediaData) {
57
+ [g_audioWriterInput appendSampleBuffer:sampleBuffer];
58
+ }
59
+ break;
60
+ }
15
61
  }
16
62
  @end
17
63
 
18
64
  @implementation ScreenCaptureKitRecorder
19
65
 
20
66
  + (BOOL)isScreenCaptureKitAvailable {
67
+ // ScreenCaptureKit'i tekrar etkinleştir - video writer ile
68
+
21
69
  if (@available(macOS 12.3, *)) {
22
70
  NSLog(@"🔍 ScreenCaptureKit availability check - macOS 12.3+ confirmed");
23
71
 
@@ -163,14 +211,49 @@ static BOOL g_isRecording = NO;
163
211
  streamConfig.showsCursor = [config[@"captureCursor"] boolValue];
164
212
  streamConfig.capturesAudio = [config[@"includeSystemAudio"] boolValue];
165
213
 
166
- // Create delegate
214
+ // Setup video writer
215
+ g_outputPath = config[@"outputPath"];
216
+ if (![self setupVideoWriterWithWidth:streamConfig.width
217
+ height:streamConfig.height
218
+ outputPath:g_outputPath
219
+ includeAudio:[config[@"includeSystemAudio"] boolValue] || [config[@"includeMicrophone"] boolValue]]) {
220
+ NSLog(@"❌ Failed to setup video writer");
221
+ contentError = [NSError errorWithDomain:@"ScreenCaptureKitError" code:-3 userInfo:@{NSLocalizedDescriptionKey: @"Video writer setup failed"}];
222
+ dispatch_semaphore_signal(semaphore);
223
+ return;
224
+ }
225
+
226
+ // Create delegate and output
167
227
  g_streamDelegate = [[ScreenCaptureKitRecorderDelegate alloc] init];
228
+ g_streamOutput = [[ScreenCaptureKitStreamOutput alloc] init];
168
229
 
169
230
  // Create and start stream
170
231
  g_stream = [[SCStream alloc] initWithFilter:filter
171
232
  configuration:streamConfig
172
233
  delegate:g_streamDelegate];
173
234
 
235
+ // Add stream output using correct API
236
+ NSError *outputError = nil;
237
+ BOOL outputAdded = [g_stream addStreamOutput:g_streamOutput
238
+ type:SCStreamOutputTypeScreen
239
+ sampleHandlerQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
240
+ error:&outputError];
241
+ if (!outputAdded) {
242
+ NSLog(@"❌ Failed to add screen output: %@", outputError);
243
+ }
244
+
245
+ if ([config[@"includeSystemAudio"] boolValue]) {
246
+ if (@available(macOS 13.0, *)) {
247
+ BOOL audioOutputAdded = [g_stream addStreamOutput:g_streamOutput
248
+ type:SCStreamOutputTypeAudio
249
+ sampleHandlerQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
250
+ error:&outputError];
251
+ if (!audioOutputAdded) {
252
+ NSLog(@"❌ Failed to add audio output: %@", outputError);
253
+ }
254
+ }
255
+ }
256
+
174
257
  [g_stream startCaptureWithCompletionHandler:^(NSError *streamError) {
175
258
  if (streamError) {
176
259
  NSLog(@"❌ Failed to start ScreenCaptureKit recording: %@", streamError);
@@ -225,9 +308,28 @@ static BOOL g_isRecording = NO;
225
308
  } else {
226
309
  NSLog(@"ScreenCaptureKit recording stopped successfully");
227
310
  }
311
+
312
+ // Finalize video file
313
+ if (g_assetWriter && g_assetWriter.status == AVAssetWriterStatusWriting) {
314
+ [g_videoWriterInput markAsFinished];
315
+ if (g_audioWriterInput) {
316
+ [g_audioWriterInput markAsFinished];
317
+ }
318
+ [g_assetWriter finishWritingWithCompletionHandler:^{
319
+ NSLog(@"✅ ScreenCaptureKit video file finalized: %@", g_outputPath);
320
+
321
+ // Cleanup
322
+ g_assetWriter = nil;
323
+ g_videoWriterInput = nil;
324
+ g_audioWriterInput = nil;
325
+ g_outputPath = nil;
326
+ }];
327
+ }
328
+
228
329
  g_isRecording = NO;
229
330
  g_stream = nil;
230
331
  g_streamDelegate = nil;
332
+ g_streamOutput = nil;
231
333
  }];
232
334
  }
233
335
  }
@@ -237,4 +339,68 @@ static BOOL g_isRecording = NO;
237
339
  return g_isRecording;
238
340
  }
239
341
 
342
+ + (BOOL)setupVideoWriterWithWidth:(NSInteger)width
343
+ height:(NSInteger)height
344
+ outputPath:(NSString *)outputPath
345
+ includeAudio:(BOOL)includeAudio {
346
+
347
+ // Create asset writer
348
+ NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
349
+ NSError *error = nil;
350
+ g_assetWriter = [[AVAssetWriter alloc] initWithURL:outputURL fileType:AVFileTypeQuickTimeMovie error:&error];
351
+
352
+ if (error || !g_assetWriter) {
353
+ NSLog(@"❌ Failed to create asset writer: %@", error);
354
+ return NO;
355
+ }
356
+
357
+ // Video writer input
358
+ NSDictionary *videoSettings = @{
359
+ AVVideoCodecKey: AVVideoCodecTypeH264,
360
+ AVVideoWidthKey: @(width),
361
+ AVVideoHeightKey: @(height),
362
+ AVVideoCompressionPropertiesKey: @{
363
+ AVVideoAverageBitRateKey: @(width * height * 8), // 8 bits per pixel
364
+ AVVideoExpectedSourceFrameRateKey: @(60)
365
+ }
366
+ };
367
+
368
+ g_videoWriterInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
369
+ g_videoWriterInput.expectsMediaDataInRealTime = YES;
370
+
371
+ if (![g_assetWriter canAddInput:g_videoWriterInput]) {
372
+ NSLog(@"❌ Cannot add video input to asset writer");
373
+ return NO;
374
+ }
375
+ [g_assetWriter addInput:g_videoWriterInput];
376
+
377
+ // Audio writer input (if needed)
378
+ if (includeAudio) {
379
+ NSDictionary *audioSettings = @{
380
+ AVFormatIDKey: @(kAudioFormatMPEG4AAC),
381
+ AVSampleRateKey: @(44100.0),
382
+ AVNumberOfChannelsKey: @(2),
383
+ AVEncoderBitRateKey: @(128000)
384
+ };
385
+
386
+ g_audioWriterInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:audioSettings];
387
+ g_audioWriterInput.expectsMediaDataInRealTime = YES;
388
+
389
+ if ([g_assetWriter canAddInput:g_audioWriterInput]) {
390
+ [g_assetWriter addInput:g_audioWriterInput];
391
+ }
392
+ }
393
+
394
+ // Start writing
395
+ if (![g_assetWriter startWriting]) {
396
+ NSLog(@"❌ Failed to start writing: %@", g_assetWriter.error);
397
+ return NO;
398
+ }
399
+
400
+ [g_assetWriter startSessionAtSourceTime:kCMTimeZero];
401
+ NSLog(@"✅ ScreenCaptureKit video writer setup complete: %@", outputPath);
402
+
403
+ return YES;
404
+ }
405
+
240
406
  @end