@vidtreo/recorder 0.0.1 → 0.8.0
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 +573 -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,653 @@ 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`
|
|
23
136
|
|
|
24
|
-
|
|
25
|
-
const fileInput = document.querySelector('input[type="file"]');
|
|
26
|
-
const file = fileInput.files[0];
|
|
27
|
-
const result = await transcodeVideo(file);
|
|
137
|
+
Mutes the audio track during recording.
|
|
28
138
|
|
|
29
|
-
|
|
30
|
-
|
|
139
|
+
**Throws:** `Error` if mute functionality is disabled (`enableMute === false`)
|
|
140
|
+
|
|
141
|
+
##### `unmuteAudio(): void`
|
|
142
|
+
|
|
143
|
+
Unmutes the audio track during recording.
|
|
144
|
+
|
|
145
|
+
**Throws:** `Error` if mute functionality is disabled (`enableMute === false`)
|
|
146
|
+
|
|
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
|
+
```
|
|
249
|
+
|
|
250
|
+
### RecordingState
|
|
55
251
|
|
|
56
|
-
|
|
252
|
+
```typescript
|
|
253
|
+
type RecordingState = 'idle' | 'countdown' | 'recording';
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### AvailableDevices
|
|
57
257
|
|
|
58
258
|
```typescript
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
width: 1280, // 1280 pixels
|
|
63
|
-
height: 720, // 720 pixels
|
|
64
|
-
bitrate: 500000, // 500kbps
|
|
65
|
-
audioCodec: 'aac', // AAC audio codec
|
|
66
|
-
preset: 'medium', // Medium quality preset
|
|
67
|
-
packetCount: 1200, // Maximum packet count for video track
|
|
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
|
|
93
306
|
|
|
94
|
-
|
|
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
|
+
});
|
|
316
|
+
|
|
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);
|
|
120
361
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
362
|
+
setTimeout(async () => {
|
|
363
|
+
await recorder.stopRecording();
|
|
364
|
+
recorder.cleanup();
|
|
365
|
+
}, 15000);
|
|
366
|
+
```
|
|
125
367
|
|
|
126
|
-
|
|
368
|
+
### Recording with Pause and Resume
|
|
127
369
|
|
|
128
|
-
|
|
370
|
+
```typescript
|
|
371
|
+
const recorder = new VidtreoRecorder({
|
|
372
|
+
apiKey: 'your-api-key',
|
|
373
|
+
apiUrl: 'https://api.example.com',
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
await recorder.startRecording({}, 'camera');
|
|
377
|
+
|
|
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
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
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
|
+
});
|
|
404
|
+
|
|
405
|
+
await recorder.startRecording({}, 'camera');
|
|
406
|
+
setTimeout(async () => {
|
|
407
|
+
await recorder.stopRecording();
|
|
408
|
+
recorder.cleanup();
|
|
409
|
+
}, 5000);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Recording with Custom Metadata
|
|
129
413
|
|
|
130
414
|
```typescript
|
|
131
|
-
|
|
132
|
-
|
|
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
|
+
});
|
|
133
424
|
|
|
134
|
-
|
|
135
|
-
|
|
425
|
+
await recorder.startRecording({}, 'camera');
|
|
426
|
+
setTimeout(async () => {
|
|
427
|
+
await recorder.stopRecording();
|
|
428
|
+
recorder.cleanup();
|
|
429
|
+
}, 5000);
|
|
136
430
|
```
|
|
137
431
|
|
|
138
|
-
###
|
|
432
|
+
### Recording with Countdown
|
|
139
433
|
|
|
140
434
|
```typescript
|
|
141
|
-
|
|
435
|
+
const recorder = new VidtreoRecorder({
|
|
436
|
+
apiKey: 'your-api-key',
|
|
437
|
+
apiUrl: 'https://api.example.com',
|
|
438
|
+
countdownDuration: 3000,
|
|
439
|
+
});
|
|
142
440
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
441
|
+
await recorder.startPreview('camera');
|
|
442
|
+
await recorder.startRecording({}, 'camera');
|
|
443
|
+
setTimeout(async () => {
|
|
444
|
+
await recorder.stopRecording();
|
|
445
|
+
recorder.cleanup();
|
|
446
|
+
}, 10000);
|
|
147
447
|
```
|
|
148
448
|
|
|
149
|
-
|
|
449
|
+
### Device Selection
|
|
450
|
+
|
|
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
|
+
```
|
|
150
467
|
|
|
151
|
-
|
|
468
|
+
### Recording State Monitoring
|
|
152
469
|
|
|
153
|
-
|
|
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
|
+
});
|
|
154
481
|
|
|
155
|
-
|
|
482
|
+
await recorder.startRecording({}, 'camera');
|
|
156
483
|
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
192
541
|
|
|
193
|
-
|
|
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
|
+
});
|
|
551
|
+
|
|
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
|
+
}
|
|
559
|
+
|
|
560
|
+
try {
|
|
561
|
+
recorder.pauseRecording();
|
|
562
|
+
} catch (error) {
|
|
563
|
+
console.error('Pause is disabled:', error.message);
|
|
564
|
+
}
|
|
198
565
|
|
|
199
|
-
|
|
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:
|
|
220
632
|
|
|
221
|
-
-
|
|
222
|
-
-
|
|
223
|
-
-
|
|
224
|
-
-
|
|
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:
|
|
645
|
+
|
|
646
|
+
- MediaDevices API for camera and microphone access
|
|
647
|
+
- MediaRecorder API for recording
|
|
648
|
+
- Screen Capture API for screen recording
|
|
649
|
+
- OffscreenCanvas API for video processing
|
|
650
|
+
- Web Audio API for audio processing
|
|
651
|
+
|
|
652
|
+
Supported browsers:
|
|
653
|
+
- Chrome 94+
|
|
654
|
+
- Firefox 92+
|
|
655
|
+
- Safari 15.4+
|
|
656
|
+
- Edge 94+
|
|
225
657
|
|
|
226
658
|
## License
|
|
227
659
|
|
|
228
660
|
MIT
|
|
229
|
-
|