node-mac-recorder 2.16.12 โ 2.16.13
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐งช Testing
|
|
4
|
+
"Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐งช Testing Electron crash fix (macOS 14 simulation)...'');\nconst MacRecorder = require(''./index.js'');\nconst recorder = new MacRecorder();\n\nrecorder.on(''recordingStarted'', (details) => {\n console.log(''โ
Recording started safely'');\n});\n\nrecorder.on(''stopped'', () => {\n console.log(''โ
Recording stopped without crash'');\n});\n\nlet recordingStarted = false;\nrecorder.startRecording(''/tmp/crash-fix-test.mov'')\n .then(success => {\n console.log(''Start result:'', success ? ''โ
SUCCESS'' : ''โ FAILED'');\n if (success) {\n recordingStarted = true;\n // Test quick start/stop cycles to stress test memory handling\n setTimeout(() => {\n console.log(''โน๏ธ Quick stop test...'');\n recorder.stopRecording().then(() => {\n console.log(''โ
Quick stop completed'');\n \n // Test immediate restart\n setTimeout(() => {\n console.log(''๐ Testing immediate restart...'');\n recorder.startRecording(''/tmp/crash-fix-test2.mov'').then(() => {\n setTimeout(() => {\n recorder.stopRecording().then(() => {\n console.log(''๐ Crash fix test completed successfully!'');\n const fs = require(''fs'');\n const files = [''/tmp/crash-fix-test.mov'', ''/tmp/crash-fix-test2.mov''];\n files.forEach(file => {\n if (fs.existsSync(file)) {\n const size = Math.round(fs.statSync(file).size/1024);\n console.log(''๐น '' + file + '':'', size + ''KB'');\n }\n });\n });\n }, 1000);\n });\n }, 500);\n });\n }, 2000);\n }\n })\n .catch(err => {\n console.error(''โ Error:'', err);\n if (recordingStarted) {\n recorder.stopRecording();\n }\n });\n\")"
|
|
5
5
|
],
|
|
6
6
|
"deny": [],
|
|
7
7
|
"ask": []
|
package/package.json
CHANGED
|
@@ -135,9 +135,20 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
135
135
|
uint64_t interval = NSEC_PER_SEC / 10; // 10 FPS for Electron stability
|
|
136
136
|
dispatch_source_set_timer(g_avTimer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, interval / 10);
|
|
137
137
|
|
|
138
|
+
// Retain objects before passing to block to prevent deallocation
|
|
139
|
+
AVAssetWriterInput *localVideoInput = g_avVideoInput;
|
|
140
|
+
AVAssetWriterInputPixelBufferAdaptor *localPixelBufferAdaptor = g_avPixelBufferAdaptor;
|
|
141
|
+
|
|
138
142
|
dispatch_source_set_event_handler(g_avTimer, ^{
|
|
139
143
|
if (!g_avIsRecording) return;
|
|
140
144
|
|
|
145
|
+
// Additional null checks for Electron safety
|
|
146
|
+
if (!localVideoInput || !localPixelBufferAdaptor) {
|
|
147
|
+
NSLog(@"โ ๏ธ Video input or pixel buffer adaptor is nil, stopping recording");
|
|
148
|
+
g_avIsRecording = false;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
141
152
|
@autoreleasepool {
|
|
142
153
|
@try {
|
|
143
154
|
// Capture screen with Electron-safe error handling
|
|
@@ -159,7 +170,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
159
170
|
|
|
160
171
|
// Convert to pixel buffer with Electron-safe error handling
|
|
161
172
|
CVPixelBufferRef pixelBuffer = nil;
|
|
162
|
-
CVReturn cvRet = CVPixelBufferPoolCreatePixelBuffer(NULL,
|
|
173
|
+
CVReturn cvRet = CVPixelBufferPoolCreatePixelBuffer(NULL, localPixelBufferAdaptor.pixelBufferPool, &pixelBuffer);
|
|
163
174
|
|
|
164
175
|
if (cvRet == kCVReturnSuccess && pixelBuffer) {
|
|
165
176
|
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
|
|
@@ -201,9 +212,9 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
201
212
|
CGContextRelease(context);
|
|
202
213
|
|
|
203
214
|
// Write frame only if input is ready
|
|
204
|
-
if (
|
|
215
|
+
if (localVideoInput && localVideoInput.readyForMoreMediaData) {
|
|
205
216
|
CMTime frameTime = CMTimeAdd(g_avStartTime, CMTimeMakeWithSeconds(g_avFrameNumber / 10.0, 600));
|
|
206
|
-
BOOL appendSuccess = [
|
|
217
|
+
BOOL appendSuccess = [localPixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:frameTime];
|
|
207
218
|
if (appendSuccess) {
|
|
208
219
|
g_avFrameNumber++;
|
|
209
220
|
} else {
|
|
@@ -253,22 +264,36 @@ extern "C" bool stopAVFoundationRecording() {
|
|
|
253
264
|
@try {
|
|
254
265
|
// Stop timer with Electron-safe cleanup
|
|
255
266
|
if (g_avTimer) {
|
|
267
|
+
// Mark as not recording FIRST to stop timer callbacks
|
|
268
|
+
g_avIsRecording = false;
|
|
269
|
+
|
|
270
|
+
// Cancel timer and wait a brief moment for completion
|
|
256
271
|
dispatch_source_cancel(g_avTimer);
|
|
272
|
+
|
|
273
|
+
// Use async to avoid deadlock in Electron
|
|
274
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
|
|
275
|
+
// Timer should be fully cancelled by now
|
|
276
|
+
});
|
|
277
|
+
|
|
257
278
|
g_avTimer = nil;
|
|
258
279
|
NSLog(@"โ
AVFoundation timer stopped safely");
|
|
259
280
|
}
|
|
260
281
|
|
|
261
|
-
// Finish writing
|
|
262
|
-
|
|
263
|
-
|
|
282
|
+
// Finish writing with null checks
|
|
283
|
+
AVAssetWriterInput *writerInput = g_avVideoInput;
|
|
284
|
+
if (writerInput) {
|
|
285
|
+
[writerInput markAsFinished];
|
|
264
286
|
}
|
|
265
287
|
|
|
266
|
-
|
|
288
|
+
AVAssetWriter *writer = g_avWriter;
|
|
289
|
+
if (writer && writer.status == AVAssetWriterStatusWriting) {
|
|
267
290
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
268
|
-
[
|
|
291
|
+
[writer finishWritingWithCompletionHandler:^{
|
|
269
292
|
dispatch_semaphore_signal(semaphore);
|
|
270
293
|
}];
|
|
271
|
-
|
|
294
|
+
// Add timeout to prevent infinite wait in Electron
|
|
295
|
+
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
|
|
296
|
+
dispatch_semaphore_wait(semaphore, timeout);
|
|
272
297
|
}
|
|
273
298
|
|
|
274
299
|
// Cleanup
|