node-mac-recorder 2.20.17 → 2.21.1

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.
@@ -0,0 +1,84 @@
1
+ # Audio Capture Guide
2
+
3
+ This guide explains how to record microphone and/or system audio alongside the silent screen recording output.
4
+
5
+ ## 1. Enumerate audio devices
6
+
7
+ ```js
8
+ const recorder = new MacRecorder();
9
+ const devices = await recorder.getAudioDevices();
10
+ /*
11
+ [
12
+ {
13
+ id: "BuiltInMicDeviceID",
14
+ name: "MacBook Pro Microphone",
15
+ manufacturer: "Apple Inc.",
16
+ isDefault: true,
17
+ transportType: 0
18
+ },
19
+ ...
20
+ ]
21
+ */
22
+ ```
23
+
24
+ Pick the `id` you want to capture. For system audio you typically need to select a loopback device (e.g. BlackHole, Loopback, VB-Cable).
25
+
26
+ ## 2. Configure the capture
27
+
28
+ ```js
29
+ recorder.setAudioSettings({
30
+ microphone: true,
31
+ systemAudio: true,
32
+ });
33
+
34
+ recorder.setSystemAudioDevice("LoopbackDeviceID"); // optional
35
+ recorder.setAudioDevice("BuiltInMicDeviceID"); // microphone device – use setOptions or setAudioSettings
36
+ ```
37
+
38
+ If you only want system audio, disable the microphone flag. When both flags are true on macOS 15+, ScreenCaptureKit mixes the feeds automatically. On older macOS versions (where the module falls back to AVFoundation) you should provide a loopback device that already mixes both sources.
39
+
40
+ ## 3. Start recording
41
+
42
+ ```js
43
+ await recorder.startRecording("./output.mov", {
44
+ includeMicrophone: true,
45
+ includeSystemAudio: true,
46
+ systemAudioDeviceId: "LoopbackDeviceID",
47
+ audioDeviceId: "BuiltInMicDeviceID",
48
+ });
49
+ ```
50
+
51
+ The recorder automatically creates `temp_audio_<timestamp>.webm` next to the main video file. The timestamp matches the cursor and camera companion files so you can synchronise them later.
52
+
53
+ ### File format fallback
54
+
55
+ - macOS 15+ → Opus audio inside a WebM container.
56
+ - macOS 13–14 → Apple does not expose a WebM audio writer, so the clip is stored in a QuickTime container while keeping the `.webm` extension (transcode if you require a strict WebM file).
57
+
58
+ ## 4. Stop recording
59
+
60
+ ```js
61
+ const result = await recorder.stopRecording();
62
+ console.log(result.audioOutputPath); // temp_audio_<timestamp>.webm
63
+ ```
64
+
65
+ The `audioCaptureStarted` / `audioCaptureStopped` events fire with the resolved path and shared session timestamp, letting you update your Electron UI instantly.
66
+
67
+ ## 5. Status helpers
68
+
69
+ ```js
70
+ const status = recorder.getAudioCaptureStatus();
71
+ // {
72
+ // isCapturing: true,
73
+ // outputFile: "/tmp/temp_audio_1720000000000.webm",
74
+ // deviceIds: { microphone: "...", system: "..." },
75
+ // includeMicrophone: true,
76
+ // includeSystemAudio: true,
77
+ // sessionTimestamp: 1720000000000
78
+ // }
79
+ ```
80
+
81
+ ## 6. Limitations
82
+
83
+ - On macOS versions that fall back to AVFoundation (13/14), system audio capture requires a virtual loopback device. The module records whichever device you select as `systemAudioDeviceId`.
84
+ - ScreenCaptureKit automatically mixes microphone + system audio when both flags are enabled. If you need isolated stems, run separate recordings with different device selections.
@@ -0,0 +1,77 @@
1
+ # Camera Capture Guide
2
+
3
+ This guide shows how to capture an external or built‑in camera feed alongside the standard screen recording workflow.
4
+
5
+ ## 1. Discover available cameras
6
+
7
+ Use the new helper to list all video-capable devices. Each entry includes the best resolution Apple reports for that device.
8
+
9
+ ```js
10
+ const cameras = await recorder.getCameraDevices();
11
+ /* Example entry:
12
+ {
13
+ id: "BuiltInCameraID",
14
+ name: "FaceTime HD Camera",
15
+ position: "front",
16
+ maxResolution: { width: 1920, height: 1080, maxFrameRate: 60 }
17
+ }
18
+ */
19
+ ```
20
+
21
+ Pick an `id` and store it for future sessions (it is stable across restarts).
22
+
23
+ ## 2. Enable camera capture
24
+
25
+ ```js
26
+ recorder.setCameraDevice(selectedCameraId); // optional – default camera is used otherwise
27
+ recorder.setCameraEnabled(true);
28
+ ```
29
+
30
+ Camera recording is completely independent from system audio and microphone settings; only video frames are captured.
31
+
32
+ The companion file is always silent—no audio tracks are written.
33
+
34
+ ## 3. Start recording
35
+
36
+ When you call `startRecording`, the module automatically creates a file named `temp_camera_<timestamp>.webm` next to the main screen recording output:
37
+
38
+ ```js
39
+ await recorder.startRecording("/tmp/output.mov", {
40
+ includeSystemAudio: true,
41
+ captureCamera: true, // shorthand for enabling camera inside options
42
+ cameraDeviceId: selectedCameraId,
43
+ });
44
+ ```
45
+
46
+ Internally this mirrors the cursor workflow: the camera file is placed in the same directory as `/tmp/output.mov`, sharing the same timestamp that the cursor JSON uses. No extra cleanup is required.
47
+
48
+ ### File format fallback
49
+
50
+ - macOS 15+ → VP9 video inside a real WebM container.
51
+ - macOS 13–14 → Apple does not expose a WebM writer, so the clip is stored in a QuickTime container even though the filename remains `.webm`. Transcode to another format if you need a strict WebM file.
52
+
53
+ The `cameraCaptureStarted` and `cameraCaptureStopped` events include the resolved path and the shared `sessionTimestamp` so the UI can react immediately.
54
+
55
+ ## 4. Stop recording
56
+
57
+ `stopRecording()` stops the screen capture and the camera recorder together:
58
+
59
+ ```js
60
+ const result = await recorder.stopRecording();
61
+ console.log(result.outputPath); // screen video
62
+ console.log(result.cameraOutputPath); // companion camera clip (or null if disabled)
63
+ ```
64
+
65
+ ## 5. Integrating with Electron live previews
66
+
67
+ Use the same `cameraDeviceId` with `navigator.mediaDevices.getUserMedia({ video: { deviceId } })` to show a live preview while the native module records in the background. The native pipeline does not consume the camera stream, so sharing the device with Electron is supported as long as the hardware allows concurrent access.
68
+
69
+ ## 6. API quick reference
70
+
71
+ - `getCameraDevices()`
72
+ - `setCameraEnabled(enabled)` / `isCameraEnabled()`
73
+ - `setCameraDevice(deviceId)`
74
+ - `getCameraCaptureStatus()` – returns `{ isCapturing, outputFile, deviceId, sessionTimestamp }`
75
+ - Events: `cameraCaptureStarted`, `cameraCaptureStopped`
76
+
77
+ With these helpers you can drive the camera UI inside an Electron app while preserving the high-resolution screen capture handled by ScreenCaptureKit or AVFoundation.
@@ -0,0 +1,58 @@
1
+ # macOS Permission Checklist
2
+
3
+ This project relies on system-level frameworks (ScreenCaptureKit, AVFoundation) that require explicit permissions. When integrating the native module into an app (Electron, standalone macOS app, etc.), make sure the host application's `Info.plist` defines the usage descriptions below.
4
+
5
+ ## Required `Info.plist` keys
6
+
7
+ ```xml
8
+ <key>NSCameraUsageDescription</key>
9
+ <string>Allow camera access for screen recording companion video.</string>
10
+
11
+ <key>NSMicrophoneUsageDescription</key>
12
+ <string>Allow microphone access for audio capture.</string>
13
+
14
+ <key>NSCameraUseContinuityCameraDeviceType</key>
15
+ <true/>
16
+
17
+ <key>com.apple.security.device.audio-input</key>
18
+ <true/>
19
+
20
+ <key>com.apple.security.device.camera</key>
21
+ <true/>
22
+ ```
23
+
24
+ > `NSCameraUseContinuityCameraDeviceType` removes the runtime warning when Continuity Camera devices are detected. macOS 14+ expects this key whenever Continuity Camera APIs are used.
25
+ > The `com.apple.security.*` entitlements are only required for sandboxed / hardened runtime builds. Omit them if your distribution does not use the macOS sandbox.
26
+
27
+ During local development you can temporarily bypass the Continuity Camera check by running with `ALLOW_CONTINUITY_CAMERA=1`, but Apple still recommends setting the Info.plist key for shipping applications.
28
+
29
+ ### Screen recording
30
+
31
+ Screen recording permissions are granted by the user via the OS **Screen Recording** privacy panel. There is no `Info.plist` key to request it, but your app should guide the user to approve it.
32
+
33
+ ## Electron apps
34
+
35
+ Add the keys above to `Info.plist` (located under `electron-builder` config or the generated app bundle). For example, with `electron-builder`, use the `mac.plist` option:
36
+
37
+ ```jsonc
38
+ // package.json
39
+ {
40
+ "build": {
41
+ "mac": {
42
+ "extendInfo": {
43
+ "NSCameraUsageDescription": "Allow camera access...",
44
+ "NSMicrophoneUsageDescription": "Allow microphone access...",
45
+ "NSCameraUseContinuityCameraDeviceType": true,
46
+ "com.apple.security.device.audio-input": true,
47
+ "com.apple.security.device.camera": true
48
+ }
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ ## Native permission prompts
55
+
56
+ The module proactively requests camera and microphone permissions when recording starts (`AVCaptureDevice requestAccess`). If access is denied, the native start call fails with an explanatory error.
57
+
58
+ Make sure to run your app once, respond to the permission prompts, and instruct testers to do the same.
package/README.md CHANGED
@@ -14,6 +14,8 @@ A powerful native macOS screen recording Node.js package with advanced window se
14
14
  - 🖱️ **Multi-Display Support** - Automatic display detection and selection
15
15
  - 🎨 **Cursor Control** - Toggle cursor visibility in recordings
16
16
  - 🖱️ **Cursor Tracking** - Track mouse position, cursor types, and click events
17
+ - 📷 **Camera Recording** - Capture silent camera video alongside screen recordings
18
+ - 🔊 **Audio Capture** - Record microphone/system audio into synchronized companion files
17
19
  - 🚫 **Automatic Overlay Exclusion** - Overlay windows automatically excluded from recordings
18
20
  - ⚡ **Electron Compatible** - Enhanced crash protection for Electron applications
19
21
 
@@ -39,6 +41,8 @@ A powerful native macOS screen recording Node.js package with advanced window se
39
41
  - 📁 **Flexible Output** - Custom output paths and formats
40
42
  - 🔐 **Permission Management** - Built-in permission checking
41
43
 
44
+ > **Note**: The screen recording container remains silent. Audio is saved separately as `temp_audio_<timestamp>.webm` so you can remix microphone and system sound in post-processing.
45
+
42
46
  ## ScreenCaptureKit Technology
43
47
 
44
48
  This package leverages Apple's modern **ScreenCaptureKit** framework (macOS 12.3+) for superior recording capabilities:
@@ -126,6 +130,10 @@ await recorder.startRecording("./recording.mov", {
126
130
  quality: "high", // 'low', 'medium', 'high'
127
131
  frameRate: 30, // FPS (15, 30, 60)
128
132
  captureCursor: false, // Show cursor (default: false)
133
+
134
+ // Camera Capture
135
+ captureCamera: true, // Enable simultaneous camera recording (video-only WebM)
136
+ cameraDeviceId: "built-in-camera-id", // Use specific camera (optional)
129
137
  });
130
138
  ```
131
139
 
@@ -136,7 +144,11 @@ Stops the current recording.
136
144
  ```javascript
137
145
  const result = await recorder.stopRecording();
138
146
  console.log("Recording saved to:", result.outputPath);
147
+ console.log("Camera clip saved to:", result.cameraOutputPath);
148
+ console.log("Audio clip saved to:", result.audioOutputPath);
149
+ console.log("Shared session timestamp:", result.sessionTimestamp);
139
150
  ```
151
+ The result object always contains `cameraOutputPath` and `audioOutputPath`. If either feature is disabled the corresponding value is `null`. The shared `sessionTimestamp` matches every automatically generated temp file name (`temp_cursor_*`, `temp_camera_*`, and `temp_audio_*`).
140
152
 
141
153
  #### `getWindows()`
142
154
 
@@ -184,15 +196,70 @@ const devices = await recorder.getAudioDevices();
184
196
  console.log(devices);
185
197
  // [
186
198
  // {
187
- // id: "device-id",
188
- // name: "Built-in Microphone",
199
+ // id: "BuiltInMicDeviceID",
200
+ // name: "MacBook Pro Microphone",
189
201
  // manufacturer: "Apple Inc.",
190
- // isDefault: true
202
+ // isDefault: true,
203
+ // transportType: 0
204
+ // },
205
+ // ...
206
+ // ]
207
+ ```
208
+
209
+ #### `getCameraDevices()`
210
+
211
+ Lists available camera devices with resolution metadata.
212
+
213
+ ```javascript
214
+ const cameras = await recorder.getCameraDevices();
215
+ console.log(cameras);
216
+ // [
217
+ // {
218
+ // id: "FaceTime HD Camera",
219
+ // name: "FaceTime HD Camera",
220
+ // position: "front",
221
+ // maxResolution: { width: 1920, height: 1080, maxFrameRate: 60 }
191
222
  // },
192
223
  // ...
193
224
  // ]
194
225
  ```
195
226
 
227
+ ### Camera Capture Helpers
228
+
229
+ - `setCameraEnabled(enabled)` – toggles simultaneous camera recording (video-only)
230
+ - `setCameraDevice(deviceId)` – selects the camera by unique macOS identifier
231
+ - `isCameraEnabled()` – returns current camera toggle state
232
+ - `getCameraCaptureStatus()` – returns `{ isCapturing, outputFile, deviceId, sessionTimestamp }`
233
+
234
+ A typical workflow looks like this:
235
+
236
+ ```javascript
237
+ const cameras = await recorder.getCameraDevices();
238
+ const selectedCamera = cameras.find((camera) => camera.position === "front") || cameras[0];
239
+
240
+ recorder.setCameraDevice(selectedCamera?.id);
241
+ recorder.setCameraEnabled(true);
242
+
243
+ await recorder.startRecording("./output.mov");
244
+ // ...
245
+ const status = recorder.getCameraCaptureStatus();
246
+ console.log("Camera stream:", status.outputFile); // temp_camera_<timestamp>.webm
247
+ await recorder.stopRecording();
248
+ ```
249
+
250
+ > The camera clip is saved alongside the screen recording as `temp_camera_<timestamp>.webm`. The file contains video frames only (no audio). Use the same device IDs in Electron to power live previews (`navigator.mediaDevices.getUserMedia({ video: { deviceId } })`). On macOS versions prior to 15, Apple does not expose a WebM encoder; the module falls back to a QuickTime container while keeping the same filename, so transcode or rename if an actual WebM container is required.
251
+
252
+ See `CAMERA_CAPTURE.md` for a deeper walkthrough and Electron integration tips.
253
+
254
+ ### Audio Capture Helpers
255
+
256
+ - `setAudioDevice(deviceId)` – select the microphone input
257
+ - `setSystemAudioEnabled(enabled)` / `isSystemAudioEnabled()`
258
+ - `setSystemAudioDevice(deviceId)` – prefer loopback devices when you need system audio only
259
+ - `getAudioCaptureStatus()` – returns `{ isCapturing, outputFile, deviceIds, includeMicrophone, includeSystemAudio, sessionTimestamp }`
260
+
261
+ See `AUDIO_CAPTURE.md` for a step-by-step guide on enumerating devices, enabling microphone/system capture, and consuming the audio companion files.
262
+
196
263
  #### `checkPermissions()`
197
264
 
198
265
  Checks macOS recording permissions.
@@ -217,6 +284,11 @@ console.log(status);
217
284
  // {
218
285
  // isRecording: true,
219
286
  // outputPath: "./recording.mov",
287
+ // cameraOutputPath: "./temp_camera_1720000000000.webm",
288
+ // audioOutputPath: "./temp_audio_1720000000000.webm",
289
+ // cameraCapturing: true,
290
+ // audioCapturing: true,
291
+ // sessionTimestamp: 1720000000000,
220
292
  // options: { ... },
221
293
  // recordingTime: 15
222
294
  // }
@@ -800,3 +872,4 @@ MIT License - see [LICENSE](LICENSE) file for details.
800
872
  ---
801
873
 
802
874
  **Made for macOS** 🍎 | **Built with AVFoundation** 📹 | **Node.js Ready** 🚀
875
+ > 🛡️ **Permissions:** Ensure your host application's `Info.plist` declares camera and microphone usage descriptions (see `MACOS_PERMISSIONS.md`).
package/binding.gyp CHANGED
@@ -6,7 +6,8 @@
6
6
  "src/mac_recorder.mm",
7
7
  "src/screen_capture_kit.mm",
8
8
  "src/avfoundation_recorder.mm",
9
- "src/audio_capture.mm",
9
+ "src/camera_recorder.mm",
10
+ "src/audio_recorder.mm",
10
11
  "src/cursor_tracker.mm",
11
12
  "src/window_selector.mm"
12
13
  ],
@@ -44,4 +45,4 @@
44
45
  "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ]
45
46
  }
46
47
  ]
47
- }
48
+ }