node-mac-recorder 2.21.22 → 2.21.23
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/electron-safe-index.js +2 -2
- package/index.js +11 -2
- package/package.json +1 -1
- package/src/avfoundation_recorder.mm +14 -8
- package/src/electron_safe/screen_capture_electron.mm +54 -3
- package/src/mac_recorder.mm +45 -15
- package/src/screen_capture_kit.mm +18 -6
package/electron-safe-index.js
CHANGED
|
@@ -44,8 +44,8 @@ class ElectronSafeMacRecorder extends EventEmitter {
|
|
|
44
44
|
this.options = {
|
|
45
45
|
includeMicrophone: false,
|
|
46
46
|
includeSystemAudio: false,
|
|
47
|
-
quality: "
|
|
48
|
-
frameRate:
|
|
47
|
+
quality: "high",
|
|
48
|
+
frameRate: 60,
|
|
49
49
|
captureArea: null,
|
|
50
50
|
captureCursor: false,
|
|
51
51
|
showClicks: false,
|
package/index.js
CHANGED
|
@@ -43,8 +43,8 @@ class MacRecorder extends EventEmitter {
|
|
|
43
43
|
this.options = {
|
|
44
44
|
includeMicrophone: false, // Default olarak mikrofon kapalı
|
|
45
45
|
includeSystemAudio: false, // Default olarak sistem sesi kapalı - kullanıcı explicit olarak açmalı
|
|
46
|
-
quality: "
|
|
47
|
-
frameRate:
|
|
46
|
+
quality: "high",
|
|
47
|
+
frameRate: 60,
|
|
48
48
|
captureArea: null, // { x, y, width, height }
|
|
49
49
|
captureCursor: false, // Default olarak cursor gizli
|
|
50
50
|
showClicks: false,
|
|
@@ -181,6 +181,13 @@ class MacRecorder extends EventEmitter {
|
|
|
181
181
|
if (options.captureCamera !== undefined) {
|
|
182
182
|
this.options.captureCamera = options.captureCamera === true;
|
|
183
183
|
}
|
|
184
|
+
if (options.frameRate !== undefined) {
|
|
185
|
+
const fps = parseInt(options.frameRate, 10);
|
|
186
|
+
if (!Number.isNaN(fps) && fps > 0) {
|
|
187
|
+
// Clamp reasonable range 1-120
|
|
188
|
+
this.options.frameRate = Math.min(Math.max(fps, 1), 120);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
184
191
|
if (options.cameraDeviceId !== undefined) {
|
|
185
192
|
this.options.cameraDeviceId =
|
|
186
193
|
typeof options.cameraDeviceId === "string" && options.cameraDeviceId.length > 0
|
|
@@ -497,6 +504,8 @@ class MacRecorder extends EventEmitter {
|
|
|
497
504
|
captureCamera: this.options.captureCamera === true,
|
|
498
505
|
cameraDeviceId: this.options.cameraDeviceId || null,
|
|
499
506
|
sessionTimestamp,
|
|
507
|
+
frameRate: this.options.frameRate || 60,
|
|
508
|
+
quality: this.options.quality || "high",
|
|
500
509
|
};
|
|
501
510
|
|
|
502
511
|
if (cameraFilePath) {
|
package/package.json
CHANGED
|
@@ -34,7 +34,8 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
34
34
|
bool includeMicrophone,
|
|
35
35
|
bool includeSystemAudio,
|
|
36
36
|
NSString* audioDeviceId,
|
|
37
|
-
NSString* audioOutputPath
|
|
37
|
+
NSString* audioOutputPath,
|
|
38
|
+
double requestedFrameRate) {
|
|
38
39
|
|
|
39
40
|
if (g_avIsRecording) {
|
|
40
41
|
NSLog(@"❌ AVFoundation recording already in progress");
|
|
@@ -129,15 +130,20 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
129
130
|
NSLog(@"🎬 ULTRA QUALITY AVFoundation: %dx%d, bitrate=%.2fMbps",
|
|
130
131
|
(int)recordingSize.width, (int)recordingSize.height, bitrate / (1000.0 * 1000.0));
|
|
131
132
|
|
|
133
|
+
// Resolve target FPS
|
|
134
|
+
double fps = requestedFrameRate > 0 ? requestedFrameRate : 60.0;
|
|
135
|
+
if (fps < 1.0) fps = 1.0;
|
|
136
|
+
if (fps > 120.0) fps = 120.0;
|
|
137
|
+
|
|
132
138
|
NSDictionary *videoSettings = @{
|
|
133
139
|
AVVideoCodecKey: codecKey,
|
|
134
140
|
AVVideoWidthKey: @((int)recordingSize.width),
|
|
135
141
|
AVVideoHeightKey: @((int)recordingSize.height),
|
|
136
142
|
AVVideoCompressionPropertiesKey: @{
|
|
137
143
|
AVVideoAverageBitRateKey: @(bitrate),
|
|
138
|
-
AVVideoMaxKeyFrameIntervalKey: @
|
|
144
|
+
AVVideoMaxKeyFrameIntervalKey: @((int)fps),
|
|
139
145
|
AVVideoAllowFrameReorderingKey: @YES,
|
|
140
|
-
AVVideoExpectedSourceFrameRateKey: @
|
|
146
|
+
AVVideoExpectedSourceFrameRateKey: @((int)fps),
|
|
141
147
|
AVVideoQualityKey: @(0.95), // 0.0-1.0, higher is better
|
|
142
148
|
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
|
|
143
149
|
AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC
|
|
@@ -266,7 +272,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
266
272
|
}
|
|
267
273
|
}
|
|
268
274
|
|
|
269
|
-
// Start capture timer
|
|
275
|
+
// Start capture timer using target FPS
|
|
270
276
|
dispatch_queue_t captureQueue = dispatch_queue_create("AVFoundationCaptureQueue", DISPATCH_QUEUE_SERIAL);
|
|
271
277
|
g_avTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, captureQueue);
|
|
272
278
|
|
|
@@ -275,7 +281,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
275
281
|
return false;
|
|
276
282
|
}
|
|
277
283
|
|
|
278
|
-
uint64_t interval = NSEC_PER_SEC /
|
|
284
|
+
uint64_t interval = (uint64_t)(NSEC_PER_SEC / fps);
|
|
279
285
|
dispatch_source_set_timer(g_avTimer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, interval / 10);
|
|
280
286
|
|
|
281
287
|
// Retain objects before passing to block to prevent deallocation
|
|
@@ -371,7 +377,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
371
377
|
|
|
372
378
|
// Write frame only if input is ready
|
|
373
379
|
if (localVideoInput && localVideoInput.readyForMoreMediaData) {
|
|
374
|
-
CMTime frameTime = CMTimeAdd(g_avStartTime, CMTimeMakeWithSeconds(g_avFrameNumber /
|
|
380
|
+
CMTime frameTime = CMTimeAdd(g_avStartTime, CMTimeMakeWithSeconds(((double)g_avFrameNumber) / fps, 600));
|
|
375
381
|
BOOL appendSuccess = [localPixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:frameTime];
|
|
376
382
|
if (appendSuccess) {
|
|
377
383
|
g_avFrameNumber++;
|
|
@@ -401,8 +407,8 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
401
407
|
dispatch_resume(g_avTimer);
|
|
402
408
|
g_avIsRecording = true;
|
|
403
409
|
|
|
404
|
-
MRLog(@"🎥 AVFoundation recording started: %dx%d @
|
|
405
|
-
(int)recordingSize.width, (int)recordingSize.height);
|
|
410
|
+
MRLog(@"🎥 AVFoundation recording started: %dx%d @ %.0ffps",
|
|
411
|
+
(int)recordingSize.width, (int)recordingSize.height, fps);
|
|
406
412
|
|
|
407
413
|
return true;
|
|
408
414
|
|
|
@@ -121,12 +121,27 @@ static void initializeSafeQueue() {
|
|
|
121
121
|
SCDisplay *targetDisplay = nil;
|
|
122
122
|
|
|
123
123
|
if (displayId) {
|
|
124
|
+
// First, try matching by real CGDirectDisplayID
|
|
124
125
|
for (SCDisplay *display in content.displays) {
|
|
125
126
|
if (display.displayID == [displayId unsignedIntValue]) {
|
|
126
127
|
targetDisplay = display;
|
|
127
128
|
break;
|
|
128
129
|
}
|
|
129
130
|
}
|
|
131
|
+
|
|
132
|
+
// If not matched, treat provided value as index (0-based or 1-based)
|
|
133
|
+
if (!targetDisplay && content.displays.count > 0) {
|
|
134
|
+
NSUInteger count = content.displays.count;
|
|
135
|
+
NSUInteger idx0 = (NSUInteger)[displayId unsignedIntValue];
|
|
136
|
+
if (idx0 < count) {
|
|
137
|
+
targetDisplay = content.displays[idx0];
|
|
138
|
+
} else if ([displayId unsignedIntegerValue] > 0) {
|
|
139
|
+
NSUInteger idx1 = [displayId unsignedIntegerValue] - 1;
|
|
140
|
+
if (idx1 < count) {
|
|
141
|
+
targetDisplay = content.displays[idx1];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
130
145
|
}
|
|
131
146
|
|
|
132
147
|
if (!targetDisplay && content.displays.count > 0) {
|
|
@@ -154,9 +169,45 @@ static void initializeSafeQueue() {
|
|
|
154
169
|
}
|
|
155
170
|
|
|
156
171
|
// Video configuration
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
// Prefer the target display's native resolution when available
|
|
173
|
+
if (filter && [filter isKindOfClass:[SCContentFilter class]]) {
|
|
174
|
+
// Try to infer dimensions from selected display or capture area
|
|
175
|
+
NSDictionary *captureArea = options[@"captureArea"];
|
|
176
|
+
if (captureArea) {
|
|
177
|
+
config.width = (size_t)[captureArea[@"width"] doubleValue];
|
|
178
|
+
config.height = (size_t)[captureArea[@"height"] doubleValue];
|
|
179
|
+
} else {
|
|
180
|
+
// Find the selected display again to get dimensions
|
|
181
|
+
NSNumber *displayId = options[@"displayId"];
|
|
182
|
+
if (displayId) {
|
|
183
|
+
for (SCDisplay *display in content.displays) {
|
|
184
|
+
if (display.displayID == [displayId unsignedIntValue]) {
|
|
185
|
+
config.width = (size_t)display.width;
|
|
186
|
+
config.height = (size_t)display.height;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Fallback default resolution if not set above
|
|
195
|
+
if (config.width == 0 || config.height == 0) {
|
|
196
|
+
config.width = 1920;
|
|
197
|
+
config.height = 1080;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Frame rate from options (default 60)
|
|
201
|
+
NSInteger fps = 60;
|
|
202
|
+
if (options[@"frameRate"]) {
|
|
203
|
+
NSInteger v = [options[@"frameRate"] integerValue];
|
|
204
|
+
if (v > 0) {
|
|
205
|
+
if (v < 1) v = 1;
|
|
206
|
+
if (v > 120) v = 120;
|
|
207
|
+
fps = v;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
config.minimumFrameInterval = CMTimeMake(1, (int)fps);
|
|
160
211
|
config.queueDepth = 8;
|
|
161
212
|
|
|
162
213
|
// Capture area if specified
|
package/src/mac_recorder.mm
CHANGED
|
@@ -19,7 +19,8 @@ extern "C" {
|
|
|
19
19
|
bool includeMicrophone,
|
|
20
20
|
bool includeSystemAudio,
|
|
21
21
|
NSString* audioDeviceId,
|
|
22
|
-
NSString* audioOutputPath
|
|
22
|
+
NSString* audioOutputPath,
|
|
23
|
+
double frameRate);
|
|
23
24
|
bool stopAVFoundationRecording();
|
|
24
25
|
bool isAVFoundationRecording();
|
|
25
26
|
NSString* getAVFoundationAudioPath();
|
|
@@ -204,6 +205,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
204
205
|
NSString *cameraOutputPath = nil;
|
|
205
206
|
int64_t sessionTimestamp = 0;
|
|
206
207
|
NSString *audioOutputPath = nil;
|
|
208
|
+
double frameRate = 60.0;
|
|
207
209
|
|
|
208
210
|
if (info.Length() > 1 && info[1].IsObject()) {
|
|
209
211
|
Napi::Object options = info[1].As<Napi::Object>();
|
|
@@ -271,33 +273,57 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
271
273
|
if (options.Has("sessionTimestamp") && options.Get("sessionTimestamp").IsNumber()) {
|
|
272
274
|
sessionTimestamp = options.Get("sessionTimestamp").As<Napi::Number>().Int64Value();
|
|
273
275
|
}
|
|
276
|
+
|
|
277
|
+
// Frame rate
|
|
278
|
+
if (options.Has("frameRate") && options.Get("frameRate").IsNumber()) {
|
|
279
|
+
double fps = options.Get("frameRate").As<Napi::Number>().DoubleValue();
|
|
280
|
+
if (fps > 0) {
|
|
281
|
+
// Clamp to reasonable range
|
|
282
|
+
if (fps < 1.0) fps = 1.0;
|
|
283
|
+
if (fps > 120.0) fps = 120.0;
|
|
284
|
+
frameRate = fps;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
274
287
|
|
|
275
|
-
// Display ID
|
|
288
|
+
// Display ID (accepts either real CGDirectDisplayID or index [0-based or 1-based])
|
|
276
289
|
if (options.Has("displayId") && !options.Get("displayId").IsNull()) {
|
|
277
290
|
double displayIdNum = options.Get("displayId").As<Napi::Number>().DoubleValue();
|
|
278
291
|
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
displayID = (CGDirectDisplayID)displayIdNum;
|
|
292
|
+
// First, assume the provided value is a real CGDirectDisplayID
|
|
293
|
+
CGDirectDisplayID candidateID = (CGDirectDisplayID)displayIdNum;
|
|
282
294
|
|
|
283
|
-
// Verify
|
|
284
|
-
uint32_t displayCount;
|
|
295
|
+
// Verify against active displays
|
|
296
|
+
uint32_t displayCount = 0;
|
|
285
297
|
CGGetActiveDisplayList(0, NULL, &displayCount);
|
|
286
298
|
if (displayCount > 0) {
|
|
287
299
|
CGDirectDisplayID *displays = (CGDirectDisplayID*)malloc(displayCount * sizeof(CGDirectDisplayID));
|
|
288
300
|
CGGetActiveDisplayList(displayCount, displays, &displayCount);
|
|
289
301
|
|
|
290
|
-
bool
|
|
302
|
+
bool matchedByID = false;
|
|
291
303
|
for (uint32_t i = 0; i < displayCount; i++) {
|
|
292
|
-
if (displays[i] ==
|
|
293
|
-
|
|
304
|
+
if (displays[i] == candidateID) {
|
|
305
|
+
matchedByID = true;
|
|
306
|
+
displayID = candidateID;
|
|
294
307
|
break;
|
|
295
308
|
}
|
|
296
309
|
}
|
|
297
310
|
|
|
298
|
-
if (!
|
|
299
|
-
//
|
|
300
|
-
|
|
311
|
+
if (!matchedByID) {
|
|
312
|
+
// Tolerant mapping: allow passing index instead of CGDirectDisplayID
|
|
313
|
+
// Try 0-based index
|
|
314
|
+
int idx0 = (int)displayIdNum;
|
|
315
|
+
if (idx0 >= 0 && idx0 < (int)displayCount) {
|
|
316
|
+
displayID = displays[idx0];
|
|
317
|
+
} else {
|
|
318
|
+
// Try 1-based index (common in user examples)
|
|
319
|
+
int idx1 = (int)displayIdNum - 1;
|
|
320
|
+
if (idx1 >= 0 && idx1 < (int)displayCount) {
|
|
321
|
+
displayID = displays[idx1];
|
|
322
|
+
} else {
|
|
323
|
+
// Fallback to main display
|
|
324
|
+
displayID = CGMainDisplayID();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
301
327
|
}
|
|
302
328
|
|
|
303
329
|
free(displays);
|
|
@@ -400,6 +426,8 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
400
426
|
if (sessionTimestamp != 0) {
|
|
401
427
|
sckConfig[@"sessionTimestamp"] = @(sessionTimestamp);
|
|
402
428
|
}
|
|
429
|
+
// Pass requested frame rate
|
|
430
|
+
sckConfig[@"frameRate"] = @(frameRate);
|
|
403
431
|
|
|
404
432
|
if (!CGRectIsNull(captureRect)) {
|
|
405
433
|
sckConfig[@"captureRect"] = @{
|
|
@@ -511,7 +539,8 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
511
539
|
bool includeMicrophone,
|
|
512
540
|
bool includeSystemAudio,
|
|
513
541
|
NSString* audioDeviceId,
|
|
514
|
-
NSString* audioOutputPath
|
|
542
|
+
NSString* audioOutputPath,
|
|
543
|
+
double frameRate);
|
|
515
544
|
|
|
516
545
|
// CRITICAL SYNC FIX: Start camera BEFORE screen recording for perfect sync
|
|
517
546
|
// This ensures both capture their first frame at approximately the same time
|
|
@@ -529,7 +558,8 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
529
558
|
// Now start screen recording immediately after camera
|
|
530
559
|
MRLog(@"🎯 SYNC: Starting screen recording immediately");
|
|
531
560
|
bool avResult = startAVFoundationRecording(outputPath, displayID, windowID, captureRect,
|
|
532
|
-
captureCursor, includeMicrophone, includeSystemAudio,
|
|
561
|
+
captureCursor, includeMicrophone, includeSystemAudio,
|
|
562
|
+
audioDeviceId, audioOutputPath, frameRate);
|
|
533
563
|
|
|
534
564
|
if (avResult) {
|
|
535
565
|
MRLog(@"🎥 RECORDING METHOD: AVFoundation");
|
|
@@ -32,6 +32,7 @@ static BOOL g_audioWriterStarted = NO;
|
|
|
32
32
|
|
|
33
33
|
static NSInteger g_configuredSampleRate = 48000;
|
|
34
34
|
static NSInteger g_configuredChannelCount = 2;
|
|
35
|
+
static NSInteger g_targetFPS = 60;
|
|
35
36
|
|
|
36
37
|
// Frame rate debugging
|
|
37
38
|
static NSInteger g_frameCount = 0;
|
|
@@ -342,9 +343,9 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
|
|
|
342
343
|
|
|
343
344
|
NSDictionary *compressionProps = @{
|
|
344
345
|
AVVideoAverageBitRateKey: @(bitrate),
|
|
345
|
-
AVVideoMaxKeyFrameIntervalKey: @
|
|
346
|
+
AVVideoMaxKeyFrameIntervalKey: @(MAX(1, g_targetFPS)),
|
|
346
347
|
AVVideoAllowFrameReorderingKey: @YES,
|
|
347
|
-
AVVideoExpectedSourceFrameRateKey: @
|
|
348
|
+
AVVideoExpectedSourceFrameRateKey: @(MAX(1, g_targetFPS)),
|
|
348
349
|
AVVideoQualityKey: @(0.95), // 0.0-1.0, higher is better (0.95 = excellent)
|
|
349
350
|
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
|
|
350
351
|
AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC
|
|
@@ -524,6 +525,17 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
|
|
|
524
525
|
NSString *audioOutputPath = MRNormalizePath(config[@"audioOutputPath"]);
|
|
525
526
|
NSNumber *sessionTimestampNumber = config[@"sessionTimestamp"];
|
|
526
527
|
|
|
528
|
+
// Extract requested frame rate
|
|
529
|
+
NSNumber *frameRateNumber = config[@"frameRate"];
|
|
530
|
+
if (frameRateNumber && [frameRateNumber respondsToSelector:@selector(intValue)]) {
|
|
531
|
+
NSInteger fps = [frameRateNumber intValue];
|
|
532
|
+
if (fps < 1) fps = 1;
|
|
533
|
+
if (fps > 120) fps = 120;
|
|
534
|
+
g_targetFPS = fps;
|
|
535
|
+
} else {
|
|
536
|
+
g_targetFPS = 60;
|
|
537
|
+
}
|
|
538
|
+
|
|
527
539
|
MRLog(@"🎬 Starting PURE ScreenCaptureKit recording (NO AVFoundation)");
|
|
528
540
|
MRLog(@"🔧 Config: cursor=%@ mic=%@ system=%@ display=%@ window=%@ crop=%@",
|
|
529
541
|
captureCursor, includeMicrophone, includeSystemAudio, displayId, windowId, captureRect);
|
|
@@ -641,7 +653,7 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
|
|
|
641
653
|
SCStreamConfiguration *streamConfig = [[SCStreamConfiguration alloc] init];
|
|
642
654
|
streamConfig.width = recordingWidth;
|
|
643
655
|
streamConfig.height = recordingHeight;
|
|
644
|
-
streamConfig.minimumFrameInterval = CMTimeMake(1,
|
|
656
|
+
streamConfig.minimumFrameInterval = CMTimeMake(1, (int)MAX(1, g_targetFPS));
|
|
645
657
|
streamConfig.pixelFormat = kCVPixelFormatType_32BGRA;
|
|
646
658
|
streamConfig.scalesToFit = NO;
|
|
647
659
|
|
|
@@ -650,7 +662,7 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
|
|
|
650
662
|
streamConfig.queueDepth = 8; // Larger queue for smoother capture
|
|
651
663
|
}
|
|
652
664
|
|
|
653
|
-
MRLog(@"🎬 ScreenCaptureKit config: %ldx%ld @
|
|
665
|
+
MRLog(@"🎬 ScreenCaptureKit config: %ldx%ld @ %ldfps", (long)recordingWidth, (long)recordingHeight, (long)g_targetFPS);
|
|
654
666
|
|
|
655
667
|
BOOL shouldCaptureMic = includeMicrophone ? [includeMicrophone boolValue] : NO;
|
|
656
668
|
BOOL shouldCaptureSystemAudio = includeSystemAudio ? [includeSystemAudio boolValue] : NO;
|
|
@@ -735,8 +747,8 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
|
|
|
735
747
|
BOOL shouldShowCursor = captureCursor ? [captureCursor boolValue] : YES;
|
|
736
748
|
streamConfig.showsCursor = shouldShowCursor;
|
|
737
749
|
|
|
738
|
-
MRLog(@"🎥 Pure ScreenCapture config: %ldx%ld @
|
|
739
|
-
recordingWidth, recordingHeight, shouldShowCursor);
|
|
750
|
+
MRLog(@"🎥 Pure ScreenCapture config: %ldx%ld @ %ldfps, cursor=%d",
|
|
751
|
+
recordingWidth, recordingHeight, (long)g_targetFPS, shouldShowCursor);
|
|
740
752
|
|
|
741
753
|
NSError *writerError = nil;
|
|
742
754
|
if (![ScreenCaptureKitRecorder prepareVideoWriterWithWidth:recordingWidth height:recordingHeight error:&writerError]) {
|