node-mac-recorder 2.21.39 → 2.21.41
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/.claude/settings.local.json +26 -1
- package/CREAVIT_CODE_SNIPPETS.md +832 -0
- package/CREAVIT_INTEGRATION.md +590 -0
- package/CURSOR_MAPPING.md +112 -0
- package/DUAL_RECORDING_PLAN.md +243 -0
- package/MULTI_RECORDING.md +270 -0
- package/MultiWindowRecorder.js +546 -0
- package/README.md +51 -0
- package/binding.gyp +1 -0
- package/index-multiprocess.js +238 -0
- package/index.js +174 -19
- package/package.json +1 -1
- package/recorder-worker.js +399 -0
- package/src/audio_mixer.mm +269 -0
- package/src/audio_recorder.mm +9 -0
- package/src/camera_recorder.mm +452 -260
- package/src/cursor_tracker.mm +75 -60
- package/src/mac_recorder.mm +279 -68
- package/src/screen_capture_kit.h +18 -5
- package/src/screen_capture_kit.mm +968 -387
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Dual/Multiple Window Recording Implementation Plan
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
Current implementation uses **global state** in native code, allowing only ONE recording at a time.
|
|
5
|
+
|
|
6
|
+
## Goal
|
|
7
|
+
Support **multiple simultaneous recordings** - each with its own:
|
|
8
|
+
- Window/Display target
|
|
9
|
+
- Output file (temp_screen_0_xxx.mov, temp_screen_1_xxx.mov, etc.)
|
|
10
|
+
- Independent start/stop control
|
|
11
|
+
|
|
12
|
+
## Required Changes
|
|
13
|
+
|
|
14
|
+
### 1. Native Code Refactoring (screen_capture_kit.mm)
|
|
15
|
+
|
|
16
|
+
#### Current (Global State):
|
|
17
|
+
```objc
|
|
18
|
+
static SCStream *g_stream = nil;
|
|
19
|
+
static BOOL g_isRecording = NO;
|
|
20
|
+
static NSString *g_outputPath = nil;
|
|
21
|
+
static AVAssetWriter *g_videoWriter = nil;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
#### New (Session-Based State):
|
|
25
|
+
```objc
|
|
26
|
+
// Recording session structure
|
|
27
|
+
@interface RecordingSession : NSObject
|
|
28
|
+
@property (nonatomic, strong) NSString *sessionId;
|
|
29
|
+
@property (nonatomic, strong) SCStream *stream;
|
|
30
|
+
@property (nonatomic, strong) NSString *outputPath;
|
|
31
|
+
@property (nonatomic, strong) AVAssetWriter *videoWriter;
|
|
32
|
+
@property (nonatomic, strong) AVAssetWriterInput *videoInput;
|
|
33
|
+
@property (nonatomic, strong) dispatch_queue_t videoQueue;
|
|
34
|
+
@property (nonatomic, assign) BOOL isRecording;
|
|
35
|
+
@property (nonatomic, assign) CMTime startTime;
|
|
36
|
+
// ... other session-specific state
|
|
37
|
+
@end
|
|
38
|
+
|
|
39
|
+
// Global session registry
|
|
40
|
+
static NSMutableDictionary<NSString *, RecordingSession *> *g_sessions = nil;
|
|
41
|
+
static dispatch_queue_t g_sessionsQueue = nil;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### Key Changes:
|
|
45
|
+
|
|
46
|
+
1. **Session Management Functions**:
|
|
47
|
+
```objc
|
|
48
|
+
+ (NSString *)createRecordingSession;
|
|
49
|
+
+ (RecordingSession *)getSession:(NSString *)sessionId;
|
|
50
|
+
+ (void)removeSession:(NSString *)sessionId;
|
|
51
|
+
+ (NSArray<NSString *> *)getActiveSessions;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
2. **Modified API**:
|
|
55
|
+
```objc
|
|
56
|
+
// Old: + (BOOL)startRecordingWithConfiguration:(NSDictionary *)config
|
|
57
|
+
// New:
|
|
58
|
+
+ (NSString *)startRecordingWithConfiguration:(NSDictionary *)config
|
|
59
|
+
delegate:(id)delegate
|
|
60
|
+
error:(NSError **)error;
|
|
61
|
+
// Returns: sessionId (e.g., "rec_1762850131780")
|
|
62
|
+
|
|
63
|
+
// Old: + (void)stopRecording;
|
|
64
|
+
// New:
|
|
65
|
+
+ (BOOL)stopRecording:(NSString *)sessionId;
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
3. **Stream Output Delegates**:
|
|
69
|
+
- Each session needs its own video/audio output delegates
|
|
70
|
+
- Delegates must know which session they belong to
|
|
71
|
+
- Frame callbacks route to correct writer
|
|
72
|
+
|
|
73
|
+
### 2. JavaScript API Updates (index.js)
|
|
74
|
+
|
|
75
|
+
#### Add Session Support:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
class MacRecorder extends EventEmitter {
|
|
79
|
+
constructor() {
|
|
80
|
+
super();
|
|
81
|
+
this.sessionId = null; // Unique session ID from native
|
|
82
|
+
this.isRecording = false;
|
|
83
|
+
// ... existing code
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async startRecording(outputPath, options = {}) {
|
|
87
|
+
// ... existing setup code ...
|
|
88
|
+
|
|
89
|
+
// Start native recording with session support
|
|
90
|
+
const recordingOptions = {
|
|
91
|
+
...options,
|
|
92
|
+
// Request specific session management
|
|
93
|
+
createNewSession: true
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Native returns sessionId
|
|
97
|
+
const result = nativeBinding.startRecording(
|
|
98
|
+
outputPath,
|
|
99
|
+
recordingOptions
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
this.sessionId = result.sessionId;
|
|
103
|
+
this.isRecording = true;
|
|
104
|
+
// ...
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async stopRecording() {
|
|
108
|
+
if (!this.sessionId) {
|
|
109
|
+
throw new Error("No active recording session");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Stop specific session
|
|
113
|
+
const success = nativeBinding.stopRecording(this.sessionId);
|
|
114
|
+
// ...
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3. File Naming Strategy
|
|
120
|
+
|
|
121
|
+
When multiple recordings are active:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
// First recording
|
|
125
|
+
const timestamp = Date.now();
|
|
126
|
+
const outputPath1 = `temp_screen_${timestamp}.mov`;
|
|
127
|
+
|
|
128
|
+
// Second recording (same timestamp, different index)
|
|
129
|
+
const outputPath2 = `temp_screen_1_${timestamp}.mov`;
|
|
130
|
+
|
|
131
|
+
// Third recording
|
|
132
|
+
const outputPath3 = `temp_screen_2_${timestamp}.mov`;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Or use session IDs:
|
|
136
|
+
```javascript
|
|
137
|
+
const outputPath = `temp_screen_${sessionId}.mov`;
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Implementation Steps
|
|
141
|
+
|
|
142
|
+
### Phase 1: Core Session Infrastructure ✅
|
|
143
|
+
- [ ] Create RecordingSession class
|
|
144
|
+
- [ ] Add session registry (Map/Dictionary)
|
|
145
|
+
- [ ] Implement session lifecycle methods
|
|
146
|
+
- [ ] Add thread-safe session access
|
|
147
|
+
|
|
148
|
+
### Phase 2: Refactor Native Recording ⏳
|
|
149
|
+
- [ ] Update startRecording to return sessionId
|
|
150
|
+
- [ ] Modify video output delegates to use session
|
|
151
|
+
- [ ] Modify audio output delegates to use session
|
|
152
|
+
- [ ] Update stopRecording to accept sessionId
|
|
153
|
+
- [ ] Test single session (backward compatibility)
|
|
154
|
+
|
|
155
|
+
### Phase 3: Multi-Session Support 🔜
|
|
156
|
+
- [ ] Test two simultaneous recordings
|
|
157
|
+
- [ ] Test different targets (window vs display)
|
|
158
|
+
- [ ] Add session limits (max 4 simultaneous?)
|
|
159
|
+
- [ ] Handle memory/performance implications
|
|
160
|
+
|
|
161
|
+
### Phase 4: JavaScript API 🔜
|
|
162
|
+
- [ ] Update MacRecorder to use sessions
|
|
163
|
+
- [ ] Add getActiveSessions() method
|
|
164
|
+
- [ ] Add getAllRecordingStatuses() method
|
|
165
|
+
- [ ] Update documentation
|
|
166
|
+
|
|
167
|
+
### Phase 5: Testing 🧪
|
|
168
|
+
- [ ] Test dual window recording
|
|
169
|
+
- [ ] Test dual display recording
|
|
170
|
+
- [ ] Test mixed (window + display)
|
|
171
|
+
- [ ] Performance benchmarks
|
|
172
|
+
- [ ] Memory usage tests
|
|
173
|
+
|
|
174
|
+
## Technical Considerations
|
|
175
|
+
|
|
176
|
+
### Memory & Performance
|
|
177
|
+
- Each SCStream captures frames independently
|
|
178
|
+
- 2 recordings @ 1080p 60fps = ~240MB/s uncompressed
|
|
179
|
+
- Limit simultaneous recordings (recommend max 4)
|
|
180
|
+
- Add memory warnings
|
|
181
|
+
|
|
182
|
+
### Thread Safety
|
|
183
|
+
- Use dispatch_queue for session access
|
|
184
|
+
- Prevent race conditions on session creation/removal
|
|
185
|
+
- Careful with delegate callbacks
|
|
186
|
+
|
|
187
|
+
### File Naming
|
|
188
|
+
- Option A: Use indices (temp_screen_0_xxx, temp_screen_1_xxx)
|
|
189
|
+
- Option B: Use session IDs (temp_screen_rec_xxx)
|
|
190
|
+
- Option C: Let user specify base name
|
|
191
|
+
|
|
192
|
+
### Backward Compatibility
|
|
193
|
+
- Single recording should work as before
|
|
194
|
+
- Default behavior: create implicit session
|
|
195
|
+
- Advanced users: explicit session management
|
|
196
|
+
|
|
197
|
+
## Example Usage
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
const MacRecorder = require('node-mac-recorder');
|
|
201
|
+
|
|
202
|
+
async function recordTwoWindows() {
|
|
203
|
+
const recorder1 = new MacRecorder();
|
|
204
|
+
const recorder2 = new MacRecorder();
|
|
205
|
+
|
|
206
|
+
const windows = await recorder1.getWindows();
|
|
207
|
+
|
|
208
|
+
// Start both recordings
|
|
209
|
+
await recorder1.startRecording('output/window1.mov', {
|
|
210
|
+
windowId: windows[0].id
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
await recorder2.startRecording('output/window2.mov', {
|
|
214
|
+
windowId: windows[1].id
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Record for 10 seconds
|
|
218
|
+
await new Promise(r => setTimeout(r, 10000));
|
|
219
|
+
|
|
220
|
+
// Stop both
|
|
221
|
+
await recorder1.stopRecording();
|
|
222
|
+
await recorder2.stopRecording();
|
|
223
|
+
|
|
224
|
+
console.log('Both recordings complete!');
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Timeline Estimate
|
|
229
|
+
|
|
230
|
+
- **Phase 1-2**: 4-6 hours (core infrastructure)
|
|
231
|
+
- **Phase 3**: 2-3 hours (multi-session testing)
|
|
232
|
+
- **Phase 4**: 1-2 hours (JS API updates)
|
|
233
|
+
- **Phase 5**: 2-3 hours (comprehensive testing)
|
|
234
|
+
|
|
235
|
+
**Total: ~10-14 hours** for complete implementation
|
|
236
|
+
|
|
237
|
+
## Questions to Decide
|
|
238
|
+
|
|
239
|
+
1. **Session Limit**: Max how many simultaneous recordings? (Recommend 2-4)
|
|
240
|
+
2. **File Naming**: Automatic indices or user-specified?
|
|
241
|
+
3. **API Style**: Explicit sessions or implicit (current MacRecorder instances)?
|
|
242
|
+
4. **Performance**: Add automatic quality reduction for multiple streams?
|
|
243
|
+
5. **Error Handling**: What if one session fails? Stop all or continue others?
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Multi-Window/Display Recording
|
|
2
|
+
|
|
3
|
+
## 🎉 Özellik: Aynı Anda Birden Fazla Kayıt
|
|
4
|
+
|
|
5
|
+
node-mac-recorder artık **aynı anda birden fazla pencere veya ekranı** kaydetme yeteneğine sahip!
|
|
6
|
+
|
|
7
|
+
### ✨ Nasıl Çalışıyor?
|
|
8
|
+
|
|
9
|
+
**Child Process Yaklaşımı**: Her `MacRecorder` instance'ı kendi ayrı Node.js process'inde çalışır. Bu sayede:
|
|
10
|
+
|
|
11
|
+
- ✅ Native kod değişikliği **GEREKMEDİ**
|
|
12
|
+
- ✅ Her process kendi **bağımsız state**'ine sahip
|
|
13
|
+
- ✅ Gerçek **paralel kayıt** (aynı anda)
|
|
14
|
+
- ✅ Kolay kullanım - sadece yeni bir class kullan!
|
|
15
|
+
|
|
16
|
+
## 📖 Kullanım
|
|
17
|
+
|
|
18
|
+
### Basit Örnek - İki Display Kaydı
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
const MacRecorder = require('./index-multiprocess');
|
|
22
|
+
|
|
23
|
+
async function recordTwoDisplays() {
|
|
24
|
+
// Her recorder kendi process'inde çalışır
|
|
25
|
+
const recorder1 = new MacRecorder();
|
|
26
|
+
const recorder2 = new MacRecorder();
|
|
27
|
+
|
|
28
|
+
// Display'leri al
|
|
29
|
+
const displays = await recorder1.getDisplays();
|
|
30
|
+
|
|
31
|
+
// İki kaydı başlat (sırayla - ScreenCaptureKit init için)
|
|
32
|
+
await recorder1.startRecording('output/display1.mov', {
|
|
33
|
+
displayId: displays[0].id,
|
|
34
|
+
frameRate: 30
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await new Promise(r => setTimeout(r, 1000)); // Kısa bekleme
|
|
38
|
+
|
|
39
|
+
await recorder2.startRecording('output/display2.mov', {
|
|
40
|
+
displayId: displays[1]?.id || displays[0].id,
|
|
41
|
+
frameRate: 30
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// İkisi de aynı anda kaydediyor!
|
|
45
|
+
console.log('📹 İki display aynı anda kaydediliyor...');
|
|
46
|
+
|
|
47
|
+
// 10 saniye kaydet
|
|
48
|
+
await new Promise(r => setTimeout(r, 10000));
|
|
49
|
+
|
|
50
|
+
// İkisini de durdur
|
|
51
|
+
await recorder1.stopRecording();
|
|
52
|
+
await recorder2.stopRecording();
|
|
53
|
+
|
|
54
|
+
// Cleanup
|
|
55
|
+
recorder1.destroy();
|
|
56
|
+
recorder2.destroy();
|
|
57
|
+
|
|
58
|
+
console.log('✅ Kayıtlar tamamlandı!');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
recordTwoDisplays();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### İleri Seviye - Farklı Window'ları Kaydet
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
const MacRecorder = require('./index-multiprocess');
|
|
68
|
+
|
|
69
|
+
async function recordTwoWindows() {
|
|
70
|
+
const recorder1 = new MacRecorder();
|
|
71
|
+
const recorder2 = new MacRecorder();
|
|
72
|
+
|
|
73
|
+
// Açık pencereleri al
|
|
74
|
+
const windows = await recorder1.getWindows();
|
|
75
|
+
|
|
76
|
+
if (windows.length < 2) {
|
|
77
|
+
console.error('En az 2 pencere açık olmalı!');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(`Kaydedilecek pencereler:`);
|
|
82
|
+
console.log(`1. ${windows[0].appName} - ${windows[0].title}`);
|
|
83
|
+
console.log(`2. ${windows[1].appName} - ${windows[1].title}`);
|
|
84
|
+
|
|
85
|
+
// Event listeners
|
|
86
|
+
recorder1.on('recordingStarted', () => {
|
|
87
|
+
console.log('✅ Pencere 1 kaydı başladı');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
recorder2.on('recordingStarted', () => {
|
|
91
|
+
console.log('✅ Pencere 2 kaydı başladı');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// İlk pencereyi kaydet
|
|
95
|
+
await recorder1.startRecording('output/window1.mov', {
|
|
96
|
+
windowId: windows[0].id,
|
|
97
|
+
captureCursor: true,
|
|
98
|
+
frameRate: 30
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// 1 saniye bekle (ScreenCaptureKit init için)
|
|
102
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
103
|
+
|
|
104
|
+
// İkinci pencereyi kaydet
|
|
105
|
+
await recorder2.startRecording('output/window2.mov', {
|
|
106
|
+
windowId: windows[1].id,
|
|
107
|
+
captureCursor: true,
|
|
108
|
+
frameRate: 30
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Her ikisi de paralel kaydediyor!
|
|
112
|
+
await new Promise(r => setTimeout(r, 10000));
|
|
113
|
+
|
|
114
|
+
// Durdur
|
|
115
|
+
await recorder1.stopRecording();
|
|
116
|
+
await recorder2.stopRecording();
|
|
117
|
+
|
|
118
|
+
// Cleanup
|
|
119
|
+
recorder1.destroy();
|
|
120
|
+
recorder2.destroy();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
recordTwoWindows();
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 📝 Önemli Notlar
|
|
127
|
+
|
|
128
|
+
### Zamanlama (Timing)
|
|
129
|
+
|
|
130
|
+
ScreenCaptureKit'in düzgün başlaması için **kayıtlar arasında ~1 saniye** bekleme gerekli:
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
await recorder1.startRecording(...);
|
|
134
|
+
await new Promise(r => setTimeout(r, 1000)); // ⚠️ ÖNEMLİ!
|
|
135
|
+
await recorder2.startRecording(...);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Dosya Adlandırma
|
|
139
|
+
|
|
140
|
+
Her kayıt **farklı bir dosyaya** yazılmalı:
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
✅ DOĞRU:
|
|
144
|
+
recorder1.startRecording('video1.mov');
|
|
145
|
+
recorder2.startRecording('video2.mov');
|
|
146
|
+
|
|
147
|
+
❌ YANLIŞ:
|
|
148
|
+
recorder1.startRecording('video.mov');
|
|
149
|
+
recorder2.startRecording('video.mov'); // Aynı dosya!
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Performans
|
|
153
|
+
|
|
154
|
+
- **2 kayıt**: Sorunsuz çalışır
|
|
155
|
+
- **3-4 kayıt**: İyi çalışır, ancak CPU kullanımı artar
|
|
156
|
+
- **5+ kayıt**: Önerilmez - sistem yavaşlayabilir
|
|
157
|
+
|
|
158
|
+
### Cleanup
|
|
159
|
+
|
|
160
|
+
Recording bittiğinde mutlaka `destroy()` çağırın:
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
recorder.destroy(); // Worker process'i temizler
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## 🧪 Test
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Multi-process test
|
|
170
|
+
node test-multiprocess.js
|
|
171
|
+
|
|
172
|
+
# İki display aynı anda
|
|
173
|
+
node test-dual-recording.js
|
|
174
|
+
|
|
175
|
+
# İki window aynı anda (en az 2 pencere açık olmalı)
|
|
176
|
+
node test-dual-window.js
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## 🔧 Teknik Detaylar
|
|
180
|
+
|
|
181
|
+
### Mimari
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
Ana Process (Node.js)
|
|
185
|
+
├── MacRecorderMultiProcess Instance 1
|
|
186
|
+
│ └── Worker Process 1 (ayrı Node.js process)
|
|
187
|
+
│ └── Native Binding (ScreenCaptureKit)
|
|
188
|
+
│ └── Video File 1
|
|
189
|
+
│
|
|
190
|
+
└── MacRecorderMultiProcess Instance 2
|
|
191
|
+
└── Worker Process 2 (ayrı Node.js process)
|
|
192
|
+
└── Native Binding (ScreenCaptureKit)
|
|
193
|
+
└── Video File 2
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### IPC (Inter-Process Communication)
|
|
197
|
+
|
|
198
|
+
Worker process'ler ile iletişim:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
// Ana process → Worker
|
|
202
|
+
worker.send({ type: 'startRecording', data: { ... } });
|
|
203
|
+
|
|
204
|
+
// Worker → Ana process
|
|
205
|
+
process.send({ type: 'event', event: 'recordingStarted', data: { ... } });
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Global State Sorunu - ÇÖZÜLDÜ! ✅
|
|
209
|
+
|
|
210
|
+
**Eski sorun**: Native kod global state kullanıyordu → Sadece 1 kayıt
|
|
211
|
+
|
|
212
|
+
**Çözüm**: Her worker ayrı process → Her biri kendi global state'i
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
Process 1: g_isRecording = true (Video 1 kaydediyor)
|
|
216
|
+
Process 2: g_isRecording = true (Video 2 kaydediyor)
|
|
217
|
+
↑ Ayrı memory space, conflict yok!
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## ⚡ Performans İpuçları
|
|
221
|
+
|
|
222
|
+
1. **Frame Rate**: Çoklu kayıt için 30 FPS yeterli (60 yerine)
|
|
223
|
+
2. **Başlangıç Gecikmesi**: Her recorder arasında 1 saniye
|
|
224
|
+
3. **Cleanup**: Kayıt bitince mutlaka `destroy()` çağır
|
|
225
|
+
4. **Memory**: Her worker ~200MB kullanır
|
|
226
|
+
|
|
227
|
+
## 🐛 Sorun Giderme
|
|
228
|
+
|
|
229
|
+
### "Worker not ready" hatası
|
|
230
|
+
```javascript
|
|
231
|
+
// Çözüm: Worker'ın hazır olmasını bekle
|
|
232
|
+
await new Promise(r => setTimeout(r, 500));
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Sadece bir dosya oluştu
|
|
236
|
+
```javascript
|
|
237
|
+
// Çözüm: Kayıtlar arasında bekleme ekle
|
|
238
|
+
await recorder1.startRecording(...);
|
|
239
|
+
await new Promise(r => setTimeout(r, 1000)); // Ekle!
|
|
240
|
+
await recorder2.startRecording(...);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Worker crash oluyor
|
|
244
|
+
```javascript
|
|
245
|
+
// Çözüm: Event listener ekle
|
|
246
|
+
recorder.on('error', (err) => {
|
|
247
|
+
console.error('Worker error:', err);
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## 📊 Karşılaştırma
|
|
252
|
+
|
|
253
|
+
| Özellik | Tek Process (index.js) | Multi-Process (index-multiprocess.js) |
|
|
254
|
+
|---------|------------------------|---------------------------------------|
|
|
255
|
+
| Aynı anda kayıt | ❌ Hayır | ✅ Evet |
|
|
256
|
+
| Native kod değişikliği | - | ❌ Gerek yok |
|
|
257
|
+
| Memory kullanımı | Düşük | Orta (worker başına ~200MB) |
|
|
258
|
+
| Kullanım kolaylığı | Kolay | Çok kolay |
|
|
259
|
+
| Performans | En iyi | İyi |
|
|
260
|
+
|
|
261
|
+
## 🎯 Kullanım Senaryoları
|
|
262
|
+
|
|
263
|
+
1. **Çoklu Monitor Kaydı**: Her monitörü ayrı dosyaya
|
|
264
|
+
2. **Uygulama + Notlar**: Bir ekranda uygulama, diğerde notlar
|
|
265
|
+
3. **Webinar + Kamera**: Ekran + webcam ayrı ayrı
|
|
266
|
+
4. **Oyun + Chat**: Oyun penceresi + Discord ayrı
|
|
267
|
+
|
|
268
|
+
## 📄 Lisans
|
|
269
|
+
|
|
270
|
+
Bu özellik node-mac-recorder'ın bir parçasıdır ve aynı lisans altındadır.
|