@vidtreo/recorder 0.0.1 → 0.8.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.
- package/README.md +629 -142
- package/dist/audio-capture-processor.worklet.js +37 -0
- package/dist/index.d.ts +632 -760
- package/dist/index.js +595 -189
- package/dist/vidtreo-recorder.d.ts +55 -0
- package/package.json +15 -16
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @vidtreo/recorder
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Vidtreo SDK for browser-based video recording and transcoding. Similar to Ziggeo and Addpipe, Vidtreo provides enterprise-grade video processing capabilities for web applications. Features include camera and screen recording, real-time MP4 transcoding, audio level analysis, mute/pause controls, source switching, device selection, and automatic backend uploads.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,222 +8,709 @@ Video transcoding package using mediabunny. Transcodes videos to MP4 format with
|
|
|
8
8
|
npm install @vidtreo/recorder
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Quick Start
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
```typescript
|
|
14
|
+
import { VidtreoRecorder } from '@vidtreo/recorder';
|
|
15
|
+
|
|
16
|
+
const recorder = new VidtreoRecorder({
|
|
17
|
+
apiKey: 'your-api-key',
|
|
18
|
+
apiUrl: 'https://your-worker.workers.dev',
|
|
19
|
+
enableSourceSwitching: true,
|
|
20
|
+
maxRecordingTime: 300000,
|
|
21
|
+
onUploadComplete: (result) => {
|
|
22
|
+
console.log('Recording ID:', result.recordingId);
|
|
23
|
+
console.log('Video URL:', result.uploadUrl);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await recorder.startPreview('camera');
|
|
28
|
+
await recorder.startRecording({}, 'camera');
|
|
29
|
+
const result = await recorder.stopRecording();
|
|
30
|
+
recorder.cleanup();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## API Reference
|
|
34
|
+
|
|
35
|
+
### VidtreoRecorder
|
|
36
|
+
|
|
37
|
+
The main class for recording video from camera or screen sources.
|
|
38
|
+
|
|
39
|
+
#### Constructor
|
|
14
40
|
|
|
15
41
|
```typescript
|
|
16
|
-
|
|
42
|
+
new VidtreoRecorder(config: VidtreoRecorderConfig)
|
|
43
|
+
```
|
|
17
44
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
45
|
+
Creates a new recorder instance with the specified configuration.
|
|
46
|
+
|
|
47
|
+
**Parameters:**
|
|
48
|
+
|
|
49
|
+
- `config.apiKey` (required): Your API key for backend authentication
|
|
50
|
+
- `config.apiUrl` (required): Your backend API URL endpoint
|
|
51
|
+
- `config.enableSourceSwitching` (optional): Enable switching between camera and screen during recording. Default: `false`
|
|
52
|
+
- `config.enableMute` (optional): Enable mute/unmute functionality. When disabled, `muteAudio()`, `unmuteAudio()`, and `toggleMute()` will throw errors. Default: `true`
|
|
53
|
+
- `config.enablePause` (optional): Enable pause/resume functionality. When disabled, `pauseRecording()` and `resumeRecording()` will throw errors. Default: `true`
|
|
54
|
+
- `config.enableDeviceChange` (optional): Enable device selection functionality. When disabled, `getAvailableDevices()` will throw an error. Default: `true`
|
|
55
|
+
- `config.maxRecordingTime` (optional): Maximum recording duration in milliseconds. When the maximum time is reached, recording automatically stops. If not set, recording can continue indefinitely until manually stopped. Examples: `300000` (5 minutes), `600000` (10 minutes), `1800000` (30 minutes)
|
|
56
|
+
- `config.countdownDuration` (optional): Countdown duration in milliseconds before recording starts. Default: `0` (no countdown)
|
|
57
|
+
- `config.userMetadata` (optional): Custom metadata object to include with uploads
|
|
58
|
+
- `config.onUploadComplete` (optional): Callback invoked when upload completes successfully
|
|
59
|
+
- `config.onUploadProgress` (optional): Callback invoked during upload with progress value (0-1)
|
|
60
|
+
- `config.onUploadError` (optional): Callback invoked when upload fails
|
|
61
|
+
- `config.onRecordingStart` (optional): Callback invoked when recording starts
|
|
62
|
+
- `config.onRecordingStop` (optional): Callback invoked when recording stops
|
|
63
|
+
- `config.onError` (optional): Callback invoked when a stream error occurs
|
|
64
|
+
|
|
65
|
+
**Throws:** `Error` if `apiKey` or `apiUrl` are missing
|
|
66
|
+
|
|
67
|
+
#### Methods
|
|
68
|
+
|
|
69
|
+
##### `initialize(): Promise<void>`
|
|
70
|
+
|
|
71
|
+
Initializes the recorder with the provided configuration. Called automatically when needed, but can be called explicitly for early initialization.
|
|
72
|
+
|
|
73
|
+
**Returns:** `Promise<void>`
|
|
74
|
+
|
|
75
|
+
##### `startPreview(sourceType?: SourceType): Promise<MediaStream>`
|
|
76
|
+
|
|
77
|
+
Starts a preview stream from the specified source without recording.
|
|
78
|
+
|
|
79
|
+
**Parameters:**
|
|
80
|
+
|
|
81
|
+
- `sourceType` (optional): Source type to preview. Either `'camera'` or `'screen'`. Default: `'camera'`
|
|
82
|
+
|
|
83
|
+
**Returns:** `Promise<MediaStream>` - The preview media stream
|
|
84
|
+
|
|
85
|
+
**Throws:** `Error` if stream initialization fails
|
|
86
|
+
|
|
87
|
+
##### `startRecording(options?: RecordingStartOptions, sourceType?: SourceType): Promise<void>`
|
|
88
|
+
|
|
89
|
+
Starts recording from the specified source. If no preview is active, automatically starts one.
|
|
90
|
+
|
|
91
|
+
**Parameters:**
|
|
92
|
+
|
|
93
|
+
- `options` (optional): Recording options object
|
|
94
|
+
- `options.video`: Video constraints (boolean or `CameraConstraints` object)
|
|
95
|
+
- `options.audio`: Audio constraints (boolean or `MediaTrackConstraints` object)
|
|
96
|
+
- `sourceType` (optional): Source type to record from. Either `'camera'` or `'screen'`. Default: `'camera'`
|
|
97
|
+
|
|
98
|
+
**Returns:** `Promise<void>`
|
|
99
|
+
|
|
100
|
+
**Throws:** `Error` if recording cannot be started
|
|
101
|
+
|
|
102
|
+
##### `stopRecording(): Promise<RecordingStopResult>`
|
|
103
|
+
|
|
104
|
+
Stops the current recording, transcodes the video, and uploads it to the backend.
|
|
105
|
+
|
|
106
|
+
**Returns:** `Promise<RecordingStopResult>` - Object containing:
|
|
107
|
+
- `recordingId`: Unique identifier for the uploaded recording
|
|
108
|
+
- `uploadUrl`: URL where the video can be accessed
|
|
109
|
+
- `blob`: The recorded video as a Blob
|
|
110
|
+
|
|
111
|
+
**Throws:** `Error` if recording is not active or upload fails
|
|
112
|
+
|
|
113
|
+
##### `switchSource(sourceType: SourceType): Promise<void>`
|
|
114
|
+
|
|
115
|
+
Switches the recording source between camera and screen during an active recording.
|
|
116
|
+
|
|
117
|
+
**Parameters:**
|
|
118
|
+
|
|
119
|
+
- `sourceType`: Target source type. Either `'camera'` or `'screen'`
|
|
120
|
+
|
|
121
|
+
**Returns:** `Promise<void>`
|
|
122
|
+
|
|
123
|
+
**Throws:** `Error` if source switching is not enabled or if not currently recording
|
|
124
|
+
|
|
125
|
+
##### `getAvailableDevices(): Promise<AvailableDevices>`
|
|
126
|
+
|
|
127
|
+
Retrieves the list of available camera and microphone devices.
|
|
128
|
+
|
|
129
|
+
**Returns:** `Promise<AvailableDevices>` - Object containing:
|
|
130
|
+
- `video`: Array of available video input devices
|
|
131
|
+
- `audio`: Array of available audio input devices
|
|
132
|
+
|
|
133
|
+
**Throws:** `Error` if device change functionality is disabled (`enableDeviceChange === false`)
|
|
134
|
+
|
|
135
|
+
##### `muteAudio(): void`
|
|
136
|
+
|
|
137
|
+
Mutes the audio track during recording.
|
|
138
|
+
|
|
139
|
+
**Throws:** `Error` if mute functionality is disabled (`enableMute === false`)
|
|
140
|
+
|
|
141
|
+
##### `unmuteAudio(): void`
|
|
142
|
+
|
|
143
|
+
Unmutes the audio track during recording.
|
|
23
144
|
|
|
24
|
-
|
|
25
|
-
const fileInput = document.querySelector('input[type="file"]');
|
|
26
|
-
const file = fileInput.files[0];
|
|
27
|
-
const result = await transcodeVideo(file);
|
|
145
|
+
**Throws:** `Error` if mute functionality is disabled (`enableMute === false`)
|
|
28
146
|
|
|
29
|
-
|
|
30
|
-
|
|
147
|
+
##### `toggleMute(): void`
|
|
148
|
+
|
|
149
|
+
Toggles the mute state of the audio track.
|
|
150
|
+
|
|
151
|
+
**Throws:** `Error` if mute functionality is disabled (`enableMute === false`)
|
|
152
|
+
|
|
153
|
+
##### `isMuted(): boolean`
|
|
154
|
+
|
|
155
|
+
Returns the current mute state.
|
|
156
|
+
|
|
157
|
+
**Returns:** `true` if audio is muted, `false` otherwise
|
|
158
|
+
|
|
159
|
+
##### `pauseRecording(): void`
|
|
160
|
+
|
|
161
|
+
Pauses the current recording. Video frames are not captured while paused.
|
|
162
|
+
|
|
163
|
+
**Throws:** `Error` if pause functionality is disabled (`enablePause === false`)
|
|
164
|
+
|
|
165
|
+
##### `resumeRecording(): void`
|
|
166
|
+
|
|
167
|
+
Resumes a paused recording.
|
|
168
|
+
|
|
169
|
+
**Throws:** `Error` if pause functionality is disabled (`enablePause === false`)
|
|
170
|
+
|
|
171
|
+
##### `isPaused(): boolean`
|
|
172
|
+
|
|
173
|
+
Returns the current pause state.
|
|
174
|
+
|
|
175
|
+
**Returns:** `true` if recording is paused, `false` otherwise
|
|
176
|
+
|
|
177
|
+
##### `getRecordingState(): RecordingState`
|
|
178
|
+
|
|
179
|
+
Returns the current recording state.
|
|
180
|
+
|
|
181
|
+
**Returns:** One of:
|
|
182
|
+
- `'idle'`: Not recording
|
|
183
|
+
- `'countdown'`: Countdown in progress before recording
|
|
184
|
+
- `'recording'`: Actively recording
|
|
185
|
+
|
|
186
|
+
##### `getStream(): MediaStream | null`
|
|
187
|
+
|
|
188
|
+
Returns the current media stream, or `null` if no stream is active.
|
|
189
|
+
|
|
190
|
+
**Returns:** `MediaStream | null`
|
|
191
|
+
|
|
192
|
+
##### `cleanup(): void`
|
|
193
|
+
|
|
194
|
+
Cleans up all resources, stops active streams, and cancels any pending operations. Should be called when the recorder is no longer needed.
|
|
195
|
+
|
|
196
|
+
## Type Definitions
|
|
197
|
+
|
|
198
|
+
### VidtreoRecorderConfig
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
interface VidtreoRecorderConfig {
|
|
202
|
+
apiKey: string;
|
|
203
|
+
apiUrl: string;
|
|
204
|
+
enableSourceSwitching?: boolean;
|
|
205
|
+
enableMute?: boolean;
|
|
206
|
+
enablePause?: boolean;
|
|
207
|
+
enableDeviceChange?: boolean;
|
|
208
|
+
maxRecordingTime?: number;
|
|
209
|
+
countdownDuration?: number;
|
|
210
|
+
userMetadata?: Record<string, unknown>;
|
|
211
|
+
onUploadComplete?: (result: {
|
|
212
|
+
recordingId: string;
|
|
213
|
+
uploadUrl: string;
|
|
214
|
+
}) => void;
|
|
215
|
+
onUploadProgress?: (progress: number) => void;
|
|
216
|
+
onUploadError?: (error: Error) => void;
|
|
217
|
+
onRecordingStart?: () => void;
|
|
218
|
+
onRecordingStop?: () => void;
|
|
219
|
+
onError?: (error: Error) => void;
|
|
220
|
+
}
|
|
31
221
|
```
|
|
32
222
|
|
|
33
|
-
|
|
223
|
+
**Note:** For web component usage, see the [@vidtreo/recorder-wc](../recorder-wc/README.md) package. Use the `max-recording-time` attribute instead of `maxRecordingTime`. The attribute accepts a numeric value in milliseconds as a string (e.g., `max-recording-time="300000"` for 5 minutes).
|
|
224
|
+
|
|
225
|
+
### RecordingStartOptions
|
|
34
226
|
|
|
35
227
|
```typescript
|
|
36
|
-
|
|
228
|
+
interface RecordingStartOptions {
|
|
229
|
+
video?: boolean | CameraConstraints;
|
|
230
|
+
audio?: boolean | MediaTrackConstraints;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
37
233
|
|
|
38
|
-
|
|
39
|
-
const result = await transcodeVideo(videoBlob, {
|
|
40
|
-
width: 1920,
|
|
41
|
-
height: 1080,
|
|
42
|
-
fps: 60,
|
|
43
|
-
bitrate: 2000000, // 2Mbps
|
|
44
|
-
packetCount: 2000,
|
|
45
|
-
});
|
|
234
|
+
### RecordingStopResult
|
|
46
235
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
236
|
+
```typescript
|
|
237
|
+
interface RecordingStopResult {
|
|
238
|
+
recordingId: string;
|
|
239
|
+
uploadUrl: string;
|
|
240
|
+
blob: Blob;
|
|
241
|
+
}
|
|
52
242
|
```
|
|
53
243
|
|
|
54
|
-
|
|
244
|
+
### SourceType
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
type SourceType = 'camera' | 'screen';
|
|
248
|
+
```
|
|
55
249
|
|
|
56
|
-
|
|
250
|
+
### RecordingState
|
|
57
251
|
|
|
58
252
|
```typescript
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
253
|
+
type RecordingState = 'idle' | 'countdown' | 'recording';
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### AvailableDevices
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
interface AvailableDevices {
|
|
260
|
+
video: MediaDeviceInfo[];
|
|
261
|
+
audio: MediaDeviceInfo[];
|
|
68
262
|
}
|
|
69
263
|
```
|
|
70
264
|
|
|
71
|
-
###
|
|
265
|
+
### CameraConstraints
|
|
72
266
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- `packetCount`: Maximum packet count for video track metadata (optimization hint)
|
|
267
|
+
```typescript
|
|
268
|
+
interface CameraConstraints {
|
|
269
|
+
width?: number | { ideal?: number; min?: number; max?: number };
|
|
270
|
+
height?: number | { ideal?: number; min?: number; max?: number };
|
|
271
|
+
frameRate?: number | { ideal?: number; min?: number; max?: number };
|
|
272
|
+
}
|
|
273
|
+
```
|
|
81
274
|
|
|
82
|
-
##
|
|
275
|
+
## Usage Examples
|
|
83
276
|
|
|
84
|
-
###
|
|
277
|
+
### Basic Recording Workflow
|
|
85
278
|
|
|
86
|
-
|
|
279
|
+
```typescript
|
|
280
|
+
import { VidtreoRecorder } from '@vidtreo/recorder';
|
|
281
|
+
|
|
282
|
+
const recorder = new VidtreoRecorder({
|
|
283
|
+
apiKey: 'your-api-key',
|
|
284
|
+
apiUrl: 'https://api.example.com',
|
|
285
|
+
onUploadComplete: (result) => {
|
|
286
|
+
console.log('Uploaded:', result.uploadUrl);
|
|
287
|
+
},
|
|
288
|
+
});
|
|
87
289
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
290
|
+
try {
|
|
291
|
+
await recorder.startPreview('camera');
|
|
292
|
+
await recorder.startRecording({}, 'camera');
|
|
293
|
+
|
|
294
|
+
setTimeout(async () => {
|
|
295
|
+
const result = await recorder.stopRecording();
|
|
296
|
+
console.log('Recording ID:', result.recordingId);
|
|
297
|
+
recorder.cleanup();
|
|
298
|
+
}, 10000);
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error('Recording failed:', error);
|
|
301
|
+
recorder.cleanup();
|
|
302
|
+
}
|
|
303
|
+
```
|
|
91
304
|
|
|
92
|
-
|
|
305
|
+
### Recording with Maximum Time Limit
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
const recorder = new VidtreoRecorder({
|
|
309
|
+
apiKey: 'your-api-key',
|
|
310
|
+
apiUrl: 'https://api.example.com',
|
|
311
|
+
maxRecordingTime: 300000,
|
|
312
|
+
onRecordingStop: () => {
|
|
313
|
+
console.log('Recording stopped automatically after 5 minutes');
|
|
314
|
+
},
|
|
315
|
+
});
|
|
93
316
|
|
|
94
|
-
|
|
317
|
+
await recorder.startRecording({}, 'camera');
|
|
318
|
+
```
|
|
95
319
|
|
|
96
|
-
###
|
|
320
|
+
### Recording with Source Switching
|
|
97
321
|
|
|
98
322
|
```typescript
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
323
|
+
const recorder = new VidtreoRecorder({
|
|
324
|
+
apiKey: 'your-api-key',
|
|
325
|
+
apiUrl: 'https://api.example.com',
|
|
326
|
+
enableSourceSwitching: true,
|
|
327
|
+
maxRecordingTime: 600000,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
await recorder.startRecording({}, 'camera');
|
|
331
|
+
|
|
332
|
+
setTimeout(async () => {
|
|
333
|
+
await recorder.switchSource('screen');
|
|
334
|
+
}, 5000);
|
|
335
|
+
|
|
336
|
+
setTimeout(async () => {
|
|
337
|
+
await recorder.switchSource('camera');
|
|
338
|
+
}, 10000);
|
|
339
|
+
|
|
340
|
+
setTimeout(async () => {
|
|
341
|
+
const result = await recorder.stopRecording();
|
|
342
|
+
recorder.cleanup();
|
|
343
|
+
}, 15000);
|
|
103
344
|
```
|
|
104
345
|
|
|
105
|
-
|
|
346
|
+
**Note:** When `maxRecordingTime` is set, the recording will automatically stop when the time limit is reached, even if the timeout callbacks are still pending. The `onRecordingStop` callback will be invoked when the maximum time is reached.
|
|
106
347
|
|
|
107
|
-
|
|
348
|
+
### Recording with Mute Control
|
|
108
349
|
|
|
109
|
-
|
|
350
|
+
```typescript
|
|
351
|
+
const recorder = new VidtreoRecorder({
|
|
352
|
+
apiKey: 'your-api-key',
|
|
353
|
+
apiUrl: 'https://api.example.com',
|
|
354
|
+
});
|
|
110
355
|
|
|
111
|
-
|
|
112
|
-
- MP4 (.mp4, .m4v)
|
|
113
|
-
- WebM (.webm)
|
|
114
|
-
- QuickTime (.mov)
|
|
115
|
-
- Matroska (.mkv)
|
|
116
|
-
- OGG (.ogv)
|
|
117
|
-
- And more...
|
|
356
|
+
await recorder.startRecording({}, 'camera');
|
|
118
357
|
|
|
119
|
-
|
|
358
|
+
recorder.muteAudio();
|
|
359
|
+
setTimeout(() => recorder.unmuteAudio(), 5000);
|
|
360
|
+
setTimeout(() => recorder.toggleMute(), 10000);
|
|
361
|
+
|
|
362
|
+
setTimeout(async () => {
|
|
363
|
+
await recorder.stopRecording();
|
|
364
|
+
recorder.cleanup();
|
|
365
|
+
}, 15000);
|
|
366
|
+
```
|
|
120
367
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
368
|
+
### Recording with Pause and Resume
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
const recorder = new VidtreoRecorder({
|
|
372
|
+
apiKey: 'your-api-key',
|
|
373
|
+
apiUrl: 'https://api.example.com',
|
|
374
|
+
});
|
|
125
375
|
|
|
126
|
-
|
|
376
|
+
await recorder.startRecording({}, 'camera');
|
|
127
377
|
|
|
128
|
-
|
|
378
|
+
setTimeout(() => recorder.pauseRecording(), 3000);
|
|
379
|
+
setTimeout(() => recorder.resumeRecording(), 6000);
|
|
380
|
+
|
|
381
|
+
setTimeout(async () => {
|
|
382
|
+
await recorder.stopRecording();
|
|
383
|
+
recorder.cleanup();
|
|
384
|
+
}, 10000);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Recording with Upload Progress
|
|
129
388
|
|
|
130
389
|
```typescript
|
|
131
|
-
|
|
132
|
-
|
|
390
|
+
const recorder = new VidtreoRecorder({
|
|
391
|
+
apiKey: 'your-api-key',
|
|
392
|
+
apiUrl: 'https://api.example.com',
|
|
393
|
+
onUploadProgress: (progress) => {
|
|
394
|
+
const percentage = Math.round(progress * 100);
|
|
395
|
+
console.log(`Upload: ${percentage}%`);
|
|
396
|
+
},
|
|
397
|
+
onUploadComplete: (result) => {
|
|
398
|
+
console.log('Upload complete:', result.uploadUrl);
|
|
399
|
+
},
|
|
400
|
+
onUploadError: (error) => {
|
|
401
|
+
console.error('Upload failed:', error.message);
|
|
402
|
+
},
|
|
403
|
+
});
|
|
133
404
|
|
|
134
|
-
|
|
135
|
-
|
|
405
|
+
await recorder.startRecording({}, 'camera');
|
|
406
|
+
setTimeout(async () => {
|
|
407
|
+
await recorder.stopRecording();
|
|
408
|
+
recorder.cleanup();
|
|
409
|
+
}, 5000);
|
|
136
410
|
```
|
|
137
411
|
|
|
138
|
-
###
|
|
412
|
+
### Recording with Custom Metadata
|
|
139
413
|
|
|
140
414
|
```typescript
|
|
141
|
-
|
|
415
|
+
const recorder = new VidtreoRecorder({
|
|
416
|
+
apiKey: 'your-api-key',
|
|
417
|
+
apiUrl: 'https://api.example.com',
|
|
418
|
+
userMetadata: {
|
|
419
|
+
userId: 'user-123',
|
|
420
|
+
sessionId: 'session-456',
|
|
421
|
+
projectId: 'project-789',
|
|
422
|
+
},
|
|
423
|
+
});
|
|
142
424
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
425
|
+
await recorder.startRecording({}, 'camera');
|
|
426
|
+
setTimeout(async () => {
|
|
427
|
+
await recorder.stopRecording();
|
|
428
|
+
recorder.cleanup();
|
|
429
|
+
}, 5000);
|
|
147
430
|
```
|
|
148
431
|
|
|
149
|
-
|
|
432
|
+
### Recording with Countdown
|
|
150
433
|
|
|
151
|
-
|
|
434
|
+
```typescript
|
|
435
|
+
const recorder = new VidtreoRecorder({
|
|
436
|
+
apiKey: 'your-api-key',
|
|
437
|
+
apiUrl: 'https://api.example.com',
|
|
438
|
+
countdownDuration: 3000,
|
|
439
|
+
});
|
|
152
440
|
|
|
153
|
-
|
|
441
|
+
await recorder.startPreview('camera');
|
|
442
|
+
await recorder.startRecording({}, 'camera');
|
|
443
|
+
setTimeout(async () => {
|
|
444
|
+
await recorder.stopRecording();
|
|
445
|
+
recorder.cleanup();
|
|
446
|
+
}, 10000);
|
|
447
|
+
```
|
|
154
448
|
|
|
155
|
-
|
|
449
|
+
### Device Selection
|
|
156
450
|
|
|
157
|
-
```
|
|
158
|
-
|
|
451
|
+
```typescript
|
|
452
|
+
const recorder = new VidtreoRecorder({
|
|
453
|
+
apiKey: 'your-api-key',
|
|
454
|
+
apiUrl: 'https://api.example.com',
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
const devices = await recorder.getAvailableDevices();
|
|
458
|
+
console.log('Cameras:', devices.video.map(d => d.label));
|
|
459
|
+
console.log('Microphones:', devices.audio.map(d => d.label));
|
|
460
|
+
|
|
461
|
+
await recorder.startRecording({}, 'camera');
|
|
462
|
+
setTimeout(async () => {
|
|
463
|
+
await recorder.stopRecording();
|
|
464
|
+
recorder.cleanup();
|
|
465
|
+
}, 5000);
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Recording State Monitoring
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
const recorder = new VidtreoRecorder({
|
|
472
|
+
apiKey: 'your-api-key',
|
|
473
|
+
apiUrl: 'https://api.example.com',
|
|
474
|
+
onRecordingStart: () => {
|
|
475
|
+
console.log('Recording started');
|
|
476
|
+
},
|
|
477
|
+
onRecordingStop: () => {
|
|
478
|
+
console.log('Recording stopped');
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
await recorder.startRecording({}, 'camera');
|
|
483
|
+
|
|
484
|
+
const checkState = setInterval(() => {
|
|
485
|
+
const state = recorder.getRecordingState();
|
|
486
|
+
const isPaused = recorder.isPaused();
|
|
487
|
+
const isMuted = recorder.isMuted();
|
|
488
|
+
console.log(`State: ${state}, Paused: ${isPaused}, Muted: ${isMuted}`);
|
|
489
|
+
}, 1000);
|
|
490
|
+
|
|
491
|
+
setTimeout(async () => {
|
|
492
|
+
clearInterval(checkState);
|
|
493
|
+
await recorder.stopRecording();
|
|
494
|
+
recorder.cleanup();
|
|
495
|
+
}, 10000);
|
|
159
496
|
```
|
|
160
497
|
|
|
161
|
-
|
|
162
|
-
- `vidtreo-recorder.js` - The web component JavaScript bundle
|
|
163
|
-
- `vidtreo-recorder.css` - The Tailwind CSS stylesheet
|
|
498
|
+
### Screen Recording
|
|
164
499
|
|
|
165
|
-
|
|
500
|
+
```typescript
|
|
501
|
+
const recorder = new VidtreoRecorder({
|
|
502
|
+
apiKey: 'your-api-key',
|
|
503
|
+
apiUrl: 'https://api.example.com',
|
|
504
|
+
enableSourceSwitching: true,
|
|
505
|
+
});
|
|
166
506
|
|
|
167
|
-
|
|
507
|
+
await recorder.startPreview('screen');
|
|
508
|
+
await recorder.startRecording({}, 'screen');
|
|
509
|
+
setTimeout(async () => {
|
|
510
|
+
await recorder.stopRecording();
|
|
511
|
+
recorder.cleanup();
|
|
512
|
+
}, 30000);
|
|
513
|
+
```
|
|
168
514
|
|
|
169
|
-
|
|
515
|
+
### Error Handling
|
|
170
516
|
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
<!-- Use the web component -->
|
|
183
|
-
<vidtreo-recorder></vidtreo-recorder>
|
|
517
|
+
```typescript
|
|
518
|
+
const recorder = new VidtreoRecorder({
|
|
519
|
+
apiKey: 'your-api-key',
|
|
520
|
+
apiUrl: 'https://api.example.com',
|
|
521
|
+
onError: (error) => {
|
|
522
|
+
console.error('Stream error:', error.message);
|
|
523
|
+
},
|
|
524
|
+
onUploadError: (error) => {
|
|
525
|
+
console.error('Upload error:', error.message);
|
|
526
|
+
},
|
|
527
|
+
});
|
|
184
528
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
529
|
+
try {
|
|
530
|
+
await recorder.startRecording({}, 'camera');
|
|
531
|
+
const result = await recorder.stopRecording();
|
|
532
|
+
console.log('Success:', result.uploadUrl);
|
|
533
|
+
} catch (error) {
|
|
534
|
+
console.error('Recording error:', error);
|
|
535
|
+
} finally {
|
|
536
|
+
recorder.cleanup();
|
|
537
|
+
}
|
|
189
538
|
```
|
|
190
539
|
|
|
191
|
-
|
|
540
|
+
### Recording with Disabled Features
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
const recorder = new VidtreoRecorder({
|
|
544
|
+
apiKey: 'your-api-key',
|
|
545
|
+
apiUrl: 'https://api.example.com',
|
|
546
|
+
enableMute: false,
|
|
547
|
+
enablePause: false,
|
|
548
|
+
enableSourceSwitching: false,
|
|
549
|
+
enableDeviceChange: false,
|
|
550
|
+
});
|
|
192
551
|
|
|
193
|
-
|
|
552
|
+
await recorder.startRecording({}, 'camera');
|
|
194
553
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
554
|
+
try {
|
|
555
|
+
recorder.muteAudio();
|
|
556
|
+
} catch (error) {
|
|
557
|
+
console.error('Mute is disabled:', error.message);
|
|
558
|
+
}
|
|
198
559
|
|
|
199
|
-
|
|
560
|
+
try {
|
|
561
|
+
recorder.pauseRecording();
|
|
562
|
+
} catch (error) {
|
|
563
|
+
console.error('Pause is disabled:', error.message);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
setTimeout(async () => {
|
|
567
|
+
await recorder.stopRecording();
|
|
568
|
+
recorder.cleanup();
|
|
569
|
+
}, 10000);
|
|
200
570
|
```
|
|
201
571
|
|
|
202
|
-
|
|
572
|
+
## Web Component Usage
|
|
203
573
|
|
|
204
|
-
|
|
205
|
-
- Click to upload video files
|
|
206
|
-
- Real-time transcoding progress
|
|
207
|
-
- Download transcoded MP4 files
|
|
208
|
-
- Play transcoded videos in a new window
|
|
209
|
-
- Error handling and validation
|
|
574
|
+
The web component is available in a separate package: [@vidtreo/recorder-wc](../recorder-wc/README.md)
|
|
210
575
|
|
|
211
|
-
|
|
576
|
+
For web component installation, usage, and examples, please refer to the [@vidtreo/recorder-wc documentation](../recorder-wc/README.md).
|
|
212
577
|
|
|
213
|
-
|
|
578
|
+
## Advanced Usage
|
|
214
579
|
|
|
215
|
-
|
|
216
|
-
|
|
580
|
+
### Low-Level APIs
|
|
581
|
+
|
|
582
|
+
For advanced use cases, the package exports lower-level APIs that provide more granular control:
|
|
583
|
+
|
|
584
|
+
#### Transcoding
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
import { transcodeVideo, DEFAULT_TRANSCODE_CONFIG } from '@vidtreo/recorder';
|
|
588
|
+
|
|
589
|
+
const videoBlob = new Blob([videoData], { type: 'video/mp4' });
|
|
590
|
+
const result = await transcodeVideo(videoBlob, {
|
|
591
|
+
width: 1920,
|
|
592
|
+
height: 1080,
|
|
593
|
+
fps: 60,
|
|
594
|
+
bitrate: 2000000,
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
console.log('Transcoded size:', result.buffer.byteLength);
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
#### RecorderController
|
|
601
|
+
|
|
602
|
+
For fine-grained control over the recording process:
|
|
603
|
+
|
|
604
|
+
```typescript
|
|
605
|
+
import { RecorderController } from '@vidtreo/recorder';
|
|
606
|
+
|
|
607
|
+
const controller = new RecorderController({
|
|
608
|
+
recording: {
|
|
609
|
+
onStateChange: (state) => console.log('State:', state),
|
|
610
|
+
},
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
await controller.initialize({
|
|
614
|
+
apiKey: 'your-api-key',
|
|
615
|
+
backendUrl: 'https://api.example.com',
|
|
616
|
+
});
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
#### Stream Management
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
import { CameraStreamManager } from '@vidtreo/recorder';
|
|
623
|
+
|
|
624
|
+
const streamManager = new CameraStreamManager();
|
|
625
|
+
await streamManager.startStream();
|
|
626
|
+
const stream = streamManager.getStream();
|
|
217
627
|
```
|
|
218
628
|
|
|
219
|
-
|
|
629
|
+
## Configuration
|
|
630
|
+
|
|
631
|
+
Video transcoding configuration is managed through your backend API. The recorder fetches configuration using the provided `apiKey` and `apiUrl`. Default transcoding settings include:
|
|
632
|
+
|
|
633
|
+
- Format: MP4
|
|
634
|
+
- Frame rate: 30 fps
|
|
635
|
+
- Resolution: 1280x720
|
|
636
|
+
- Bitrate: 500 kbps
|
|
637
|
+
- Audio codec: AAC
|
|
638
|
+
- Preset: Medium quality
|
|
639
|
+
|
|
640
|
+
These defaults are used when backend configuration is unavailable or during initialization.
|
|
641
|
+
|
|
642
|
+
## Browser Compatibility
|
|
643
|
+
|
|
644
|
+
This package requires modern browser APIs for full functionality. The most critical requirement is support for the **WebCodecs API**, which enables real-time MP4 transcoding.
|
|
645
|
+
|
|
646
|
+
### Full Support (All Features)
|
|
647
|
+
|
|
648
|
+
The following browsers support all features including real-time MP4 transcoding:
|
|
649
|
+
|
|
650
|
+
- **Chrome 94+** - Full support for all features
|
|
651
|
+
- **Edge (Chromium) 94+** - Full support, same as Chrome
|
|
652
|
+
- **Opera 80+** - Full support, Chromium-based
|
|
653
|
+
- **Brave 1.30+** - Full support, Chromium-based
|
|
654
|
+
- **Vivaldi 5.0+** - Full support, Chromium-based
|
|
655
|
+
- **Firefox 130+** - Full WebCodecs support
|
|
656
|
+
- **Safari (macOS/iOS) 26.0+** - Full WebCodecs support
|
|
657
|
+
|
|
658
|
+
### Partial Support (Core Features Only)
|
|
220
659
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
-
|
|
224
|
-
-
|
|
660
|
+
The following browsers support core recording features but may have limitations:
|
|
661
|
+
|
|
662
|
+
- **Firefox 76-129** - AudioWorklet supported, but WebCodecs not available. Real-time MP4 transcoding unavailable; falls back to MediaRecorder (WebM format)
|
|
663
|
+
- **Safari (macOS/iOS) 16.4-25.x** - Partial WebCodecs support; some codecs may not be available
|
|
664
|
+
|
|
665
|
+
### Required Browser APIs
|
|
666
|
+
|
|
667
|
+
- **WebCodecs API** - Required for real-time MP4 transcoding (via mediabunny)
|
|
668
|
+
- **OffscreenCanvas** - Required for video frame processing
|
|
669
|
+
- **AudioWorklet** - Required for audio processing and level analysis
|
|
670
|
+
- **MediaRecorder API** - Used as fallback recording method
|
|
671
|
+
- **Screen Capture API** (`getDisplayMedia`) - Required for screen recording
|
|
672
|
+
- **MediaDevices API** (`getUserMedia`) - Required for camera/microphone access
|
|
673
|
+
- **IndexedDB** - Required for persistent upload queue
|
|
674
|
+
- **Storage API** - Required for storage quota management
|
|
675
|
+
|
|
676
|
+
### Feature Support by Browser
|
|
677
|
+
|
|
678
|
+
| Feature | Chrome 94+ | Edge 94+ | Firefox 130+ | Safari 26.0+ | Firefox 76-129 | Safari 16.4-25.x |
|
|
679
|
+
|---------|------------|---------|--------------|--------------|---------------|------------------|
|
|
680
|
+
| Camera Recording | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
681
|
+
| Screen Recording | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
682
|
+
| Real-time MP4 Transcoding | ✅ | ✅ | ✅ | ✅ | ❌ (WebM) | ⚠️ (Partial) |
|
|
683
|
+
| Audio Level Analysis | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
684
|
+
| Source Switching | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
685
|
+
| Device Switching | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
686
|
+
| Persistent Upload Queue | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
687
|
+
|
|
688
|
+
### Unsupported Browsers
|
|
689
|
+
|
|
690
|
+
- Internet Explorer (all versions)
|
|
691
|
+
- Firefox < 76 (missing AudioWorklet)
|
|
692
|
+
- Safari < 14.1 (missing AudioWorklet)
|
|
693
|
+
- Safari < 16.4 (missing OffscreenCanvas and WebCodecs)
|
|
694
|
+
- Edge Legacy (pre-Chromium)
|
|
695
|
+
- Chrome < 94 (missing WebCodecs)
|
|
696
|
+
|
|
697
|
+
### Mobile Browser Support
|
|
698
|
+
|
|
699
|
+
- **Chrome Android 94+** - Full support
|
|
700
|
+
- **Samsung Internet 18.0+** - Full support
|
|
701
|
+
- **Firefox Android 130+** - Full support
|
|
702
|
+
- **Safari iOS 16.4+** - Partial support (WebCodecs partial)
|
|
703
|
+
- **Safari iOS 26.0+** - Full support
|
|
704
|
+
|
|
705
|
+
### Important Notes
|
|
706
|
+
|
|
707
|
+
1. **HTTPS Required**: Media capture APIs require HTTPS (or localhost) in most browsers
|
|
708
|
+
2. **User Permissions**: Camera, microphone, and screen capture require explicit user permission
|
|
709
|
+
3. **Automatic Fallback**: The package automatically falls back to MediaRecorder when WebCodecs is unavailable (produces WebM instead of MP4)
|
|
710
|
+
4. **Feature Detection**: The package includes feature detection and gracefully degrades functionality when APIs are unavailable
|
|
711
|
+
|
|
712
|
+
For detailed browser compatibility information, API requirements, and known limitations, see [BROWSER_COMPATIBILITY.md](./BROWSER_COMPATIBILITY.md).
|
|
225
713
|
|
|
226
714
|
## License
|
|
227
715
|
|
|
228
716
|
MIT
|
|
229
|
-
|