@voltkit-rtc/client 0.5.0 → 0.6.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/CHANGELOG.md +7 -0
- package/README.md +38 -0
- package/dist/cjs/index.d.ts +7 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +31 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/recording.d.ts +91 -0
- package/dist/cjs/recording.d.ts.map +1 -0
- package/dist/cjs/recording.js +478 -0
- package/dist/cjs/recording.js.map +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +27 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/recording.d.ts +91 -0
- package/dist/esm/recording.d.ts.map +1 -0
- package/dist/esm/recording.js +471 -0
- package/dist/esm/recording.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +19 -0
- package/src/recording.ts +571 -0
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
export class RecordingError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
constructor(message, status = 0) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "RecordingError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
export async function recordingRequest(base, path, body, token) {
|
|
11
|
+
const response = await fetch(`${base.replace(/\/$/, "")}/recording/upload${path}`, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
credentials: "omit",
|
|
14
|
+
headers: {
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify(body),
|
|
19
|
+
signal: AbortSignal.timeout(path.endsWith("/complete") ? 180000 : 15000),
|
|
20
|
+
});
|
|
21
|
+
const result = (await response.json());
|
|
22
|
+
if (!response.ok || !result.success || !result.data)
|
|
23
|
+
throw new RecordingError(result.error?.message ?? "Recording request failed", response.status);
|
|
24
|
+
return result.data;
|
|
25
|
+
}
|
|
26
|
+
/** Bounded uploader, also usable with an externally encoded native/file source. */
|
|
27
|
+
export class RecordingUploader {
|
|
28
|
+
session;
|
|
29
|
+
options;
|
|
30
|
+
queue = [];
|
|
31
|
+
pendingBytes = 0;
|
|
32
|
+
bytes = 0;
|
|
33
|
+
uploaded = 0;
|
|
34
|
+
number = 0;
|
|
35
|
+
active = new Set();
|
|
36
|
+
reserve = Promise.resolve();
|
|
37
|
+
failure = null;
|
|
38
|
+
finishing = false;
|
|
39
|
+
sealed = false;
|
|
40
|
+
canceled = false;
|
|
41
|
+
aborter = new AbortController();
|
|
42
|
+
constructor(session, options) {
|
|
43
|
+
this.session = session;
|
|
44
|
+
this.options = options;
|
|
45
|
+
}
|
|
46
|
+
static async create(options, roomToken, mimeType) {
|
|
47
|
+
const url = new URL(options.apiUrl);
|
|
48
|
+
if (url.protocol !== "https:" &&
|
|
49
|
+
!["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))
|
|
50
|
+
throw new RecordingError("Recording API must use HTTPS");
|
|
51
|
+
const requestId = crypto.randomUUID();
|
|
52
|
+
let session;
|
|
53
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
54
|
+
try {
|
|
55
|
+
session = await recordingRequest(options.apiUrl, "/sessions", {
|
|
56
|
+
roomToken,
|
|
57
|
+
requestId,
|
|
58
|
+
mimeType,
|
|
59
|
+
...(options.chunkMiB ? { chunkMiB: options.chunkMiB } : {}),
|
|
60
|
+
});
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (attempt === 2 ||
|
|
65
|
+
(error instanceof RecordingError &&
|
|
66
|
+
![409, 429, 503].includes(error.status)))
|
|
67
|
+
throw error;
|
|
68
|
+
await delay(500 * (attempt + 1));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!session ||
|
|
72
|
+
![5 * 1048576, 10 * 1048576].includes(session.chunkBytes) ||
|
|
73
|
+
session.maxBufferBytes < session.chunkBytes * 2 ||
|
|
74
|
+
session.maxBufferBytes > 100 * 1048576 ||
|
|
75
|
+
session.uploadConcurrency < 1 ||
|
|
76
|
+
session.uploadConcurrency > 4)
|
|
77
|
+
throw new RecordingError("Invalid recording upload policy");
|
|
78
|
+
return new RecordingUploader(session, options);
|
|
79
|
+
}
|
|
80
|
+
get bufferedBytes() {
|
|
81
|
+
return this.bytes;
|
|
82
|
+
}
|
|
83
|
+
get uploadedBytes() {
|
|
84
|
+
return this.uploaded;
|
|
85
|
+
}
|
|
86
|
+
get hasCapacity() {
|
|
87
|
+
return (!this.failure &&
|
|
88
|
+
!this.canceled &&
|
|
89
|
+
this.bytes < this.session.maxBufferBytes - this.session.chunkBytes);
|
|
90
|
+
}
|
|
91
|
+
emit(state, error) {
|
|
92
|
+
try {
|
|
93
|
+
this.options.onState?.({
|
|
94
|
+
state,
|
|
95
|
+
bufferedBytes: this.bytes,
|
|
96
|
+
uploadedBytes: this.uploaded,
|
|
97
|
+
recordingId: this.session.recording.id,
|
|
98
|
+
...(error ? { error } : {}),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/* UI callbacks never break accounting/cleanup. */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
append(blob) {
|
|
106
|
+
if (this.finishing || this.canceled)
|
|
107
|
+
throw new RecordingError("Recording upload has ended");
|
|
108
|
+
if (this.failure)
|
|
109
|
+
throw this.failure;
|
|
110
|
+
if (!blob.size)
|
|
111
|
+
return;
|
|
112
|
+
if (blob.size > this.session.maxBufferBytes - this.bytes)
|
|
113
|
+
throw new RecordingError("Recording buffer limit reached; stop recording without stopping the live stream");
|
|
114
|
+
this.queue.push(blob);
|
|
115
|
+
this.pendingBytes += blob.size;
|
|
116
|
+
this.bytes += blob.size;
|
|
117
|
+
this.pump();
|
|
118
|
+
this.emit(this.hasCapacity ? "recording" : "backpressure");
|
|
119
|
+
}
|
|
120
|
+
take(size) {
|
|
121
|
+
const blobs = [];
|
|
122
|
+
let remaining = size;
|
|
123
|
+
while (remaining > 0) {
|
|
124
|
+
const blob = this.queue.shift();
|
|
125
|
+
const length = Math.min(remaining, blob.size);
|
|
126
|
+
blobs.push(blob.slice(0, length));
|
|
127
|
+
if (length < blob.size)
|
|
128
|
+
this.queue.unshift(blob.slice(length));
|
|
129
|
+
remaining -= length;
|
|
130
|
+
}
|
|
131
|
+
this.pendingBytes -= size;
|
|
132
|
+
return new Blob(blobs);
|
|
133
|
+
}
|
|
134
|
+
pump() {
|
|
135
|
+
while (!this.failure &&
|
|
136
|
+
!this.canceled &&
|
|
137
|
+
this.active.size < this.session.uploadConcurrency &&
|
|
138
|
+
this.pendingBytes >= this.session.chunkBytes)
|
|
139
|
+
this.upload(this.take(this.session.chunkBytes), false);
|
|
140
|
+
}
|
|
141
|
+
upload(blob, final) {
|
|
142
|
+
const number = ++this.number;
|
|
143
|
+
// Reserve in sequence, while PUT bodies upload concurrently.
|
|
144
|
+
const signed = this.reserve.then(() => recordingRequest(this.options.apiUrl, `/sessions/${this.session.recording.id}/parts`, { number, bytes: blob.size, final }, this.session.token));
|
|
145
|
+
this.reserve = signed;
|
|
146
|
+
let task;
|
|
147
|
+
task = (async () => {
|
|
148
|
+
let url = (await signed).url;
|
|
149
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
150
|
+
if (this.canceled)
|
|
151
|
+
throw new RecordingError("Recording canceled");
|
|
152
|
+
try {
|
|
153
|
+
const response = await fetch(url, {
|
|
154
|
+
method: "PUT",
|
|
155
|
+
body: blob,
|
|
156
|
+
credentials: "omit",
|
|
157
|
+
signal: AbortSignal.any([
|
|
158
|
+
this.aborter.signal,
|
|
159
|
+
AbortSignal.timeout(45000),
|
|
160
|
+
]),
|
|
161
|
+
});
|
|
162
|
+
if (!response.ok)
|
|
163
|
+
throw new RecordingError("R2 upload failed", response.status);
|
|
164
|
+
this.bytes -= blob.size;
|
|
165
|
+
this.uploaded += blob.size;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
if (this.canceled || attempt === 3)
|
|
170
|
+
throw error;
|
|
171
|
+
await delay(500 * 2 ** attempt);
|
|
172
|
+
url = (await recordingRequest(this.options.apiUrl, `/sessions/${this.session.recording.id}/parts`, { number, bytes: blob.size, final }, this.session.token)).url;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
})()
|
|
176
|
+
.catch((error) => {
|
|
177
|
+
this.failure =
|
|
178
|
+
error instanceof Error
|
|
179
|
+
? error
|
|
180
|
+
: new RecordingError("Recording upload failed");
|
|
181
|
+
this.emit("error", this.failure);
|
|
182
|
+
})
|
|
183
|
+
.finally(() => {
|
|
184
|
+
this.active.delete(task);
|
|
185
|
+
this.pump();
|
|
186
|
+
this.emit(this.failure
|
|
187
|
+
? "error"
|
|
188
|
+
: this.hasCapacity
|
|
189
|
+
? "recording"
|
|
190
|
+
: "backpressure");
|
|
191
|
+
});
|
|
192
|
+
this.active.add(task);
|
|
193
|
+
}
|
|
194
|
+
async waitForCapacity() {
|
|
195
|
+
while (!this.hasCapacity) {
|
|
196
|
+
if (this.failure)
|
|
197
|
+
throw this.failure;
|
|
198
|
+
if (this.canceled)
|
|
199
|
+
throw new RecordingError("Recording canceled");
|
|
200
|
+
await Promise.race(this.active);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async seal() {
|
|
204
|
+
if (this.sealed)
|
|
205
|
+
return;
|
|
206
|
+
this.finishing = true;
|
|
207
|
+
this.emit("uploading");
|
|
208
|
+
while (this.active.size) {
|
|
209
|
+
await Promise.all(this.active);
|
|
210
|
+
if (this.failure)
|
|
211
|
+
throw this.failure;
|
|
212
|
+
}
|
|
213
|
+
if (this.pendingBytes) {
|
|
214
|
+
this.upload(this.take(this.pendingBytes), true);
|
|
215
|
+
await Promise.all(this.active);
|
|
216
|
+
}
|
|
217
|
+
if (this.failure)
|
|
218
|
+
throw this.failure;
|
|
219
|
+
if (!this.number) {
|
|
220
|
+
await this.abort();
|
|
221
|
+
throw new RecordingError("No video was recorded");
|
|
222
|
+
}
|
|
223
|
+
this.sealed = true;
|
|
224
|
+
}
|
|
225
|
+
async complete() {
|
|
226
|
+
await this.seal();
|
|
227
|
+
const file = await recordingRequest(this.options.apiUrl, `/sessions/${this.session.recording.id}/complete`, {}, this.session.token);
|
|
228
|
+
this.emit("ready");
|
|
229
|
+
return file;
|
|
230
|
+
}
|
|
231
|
+
async abort() {
|
|
232
|
+
this.canceled = true;
|
|
233
|
+
this.aborter.abort();
|
|
234
|
+
await Promise.all(this.active);
|
|
235
|
+
this.queue = [];
|
|
236
|
+
this.bytes = 0;
|
|
237
|
+
this.pendingBytes = 0;
|
|
238
|
+
await recordingRequest(this.options.apiUrl, `/sessions/${this.session.recording.id}/abort`, {}, this.session.token);
|
|
239
|
+
this.emit("stopped");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/** Encodes a downscaled COPY of local camera/mic. Never changes RTC tracks. */
|
|
243
|
+
export class CameraRecording {
|
|
244
|
+
getStream;
|
|
245
|
+
getRoomToken;
|
|
246
|
+
options;
|
|
247
|
+
stopped = false;
|
|
248
|
+
stopSegment = null;
|
|
249
|
+
done = null;
|
|
250
|
+
files = new Map();
|
|
251
|
+
finalizers = new Set();
|
|
252
|
+
finalFailure = null;
|
|
253
|
+
segmentNumber = 0;
|
|
254
|
+
constructor(getStream, getRoomToken, options) {
|
|
255
|
+
this.getStream = getStream;
|
|
256
|
+
this.getRoomToken = getRoomToken;
|
|
257
|
+
this.options = options;
|
|
258
|
+
}
|
|
259
|
+
start() {
|
|
260
|
+
const stream = this.getStream();
|
|
261
|
+
if (!stream
|
|
262
|
+
?.getVideoTracks()
|
|
263
|
+
.some((track) => track.readyState === "live" && track.enabled))
|
|
264
|
+
throw new RecordingError("Publish an enabled camera before recording");
|
|
265
|
+
if (this.done)
|
|
266
|
+
throw new RecordingError("Recording is already started");
|
|
267
|
+
let ready, reject;
|
|
268
|
+
const started = new Promise((yes, no) => {
|
|
269
|
+
ready = yes;
|
|
270
|
+
reject = no;
|
|
271
|
+
});
|
|
272
|
+
this.done = this.run(ready)
|
|
273
|
+
.then((files) => {
|
|
274
|
+
ready();
|
|
275
|
+
return files;
|
|
276
|
+
})
|
|
277
|
+
.catch((error) => {
|
|
278
|
+
reject(error);
|
|
279
|
+
this.stopped = true;
|
|
280
|
+
try {
|
|
281
|
+
this.options.onState?.({
|
|
282
|
+
state: "error",
|
|
283
|
+
bufferedBytes: 0,
|
|
284
|
+
uploadedBytes: 0,
|
|
285
|
+
error: error instanceof Error
|
|
286
|
+
? error
|
|
287
|
+
: new RecordingError("Recording failed"),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
catch { }
|
|
291
|
+
throw error;
|
|
292
|
+
});
|
|
293
|
+
void this.done.catch(() => undefined);
|
|
294
|
+
return started;
|
|
295
|
+
}
|
|
296
|
+
async stop() {
|
|
297
|
+
this.stopped = true;
|
|
298
|
+
this.stopSegment?.();
|
|
299
|
+
return this.done ?? [];
|
|
300
|
+
}
|
|
301
|
+
async run(ready) {
|
|
302
|
+
while (!this.stopped) {
|
|
303
|
+
const stream = this.getStream(), camera = stream?.getVideoTracks()[0];
|
|
304
|
+
if (!stream || !camera || camera.readyState !== "live" || !camera.enabled)
|
|
305
|
+
break;
|
|
306
|
+
const mimeType = ["video/webm;codecs=vp8,opus", "video/mp4"].find((type) => MediaRecorder.isTypeSupported(type));
|
|
307
|
+
if (!mimeType)
|
|
308
|
+
throw new RecordingError("This browser cannot encode supported recording video");
|
|
309
|
+
const uploader = await RecordingUploader.create(this.options, this.getRoomToken(), mimeType);
|
|
310
|
+
let video = null, encoded = null, timer, segmentTimer;
|
|
311
|
+
try {
|
|
312
|
+
if (this.stopped) {
|
|
313
|
+
await uploader.abort();
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
const policy = uploader.session.recording;
|
|
317
|
+
video = document.createElement("video");
|
|
318
|
+
video.srcObject = stream;
|
|
319
|
+
video.muted = true;
|
|
320
|
+
video.playsInline = true;
|
|
321
|
+
await video.play();
|
|
322
|
+
if (this.stopped) {
|
|
323
|
+
await uploader.abort();
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
const portrait = (video.videoHeight || camera.getSettings().height || policy.height) >
|
|
327
|
+
(video.videoWidth || camera.getSettings().width || policy.width);
|
|
328
|
+
const canvas = document.createElement("canvas");
|
|
329
|
+
canvas.width = portrait ? policy.height : policy.width;
|
|
330
|
+
canvas.height = portrait ? policy.width : policy.height;
|
|
331
|
+
const context = canvas.getContext("2d");
|
|
332
|
+
if (!context)
|
|
333
|
+
throw new RecordingError("Recording canvas unavailable");
|
|
334
|
+
encoded = canvas.captureStream(policy.frameRate);
|
|
335
|
+
for (const track of stream.getAudioTracks())
|
|
336
|
+
encoded.addTrack(track.clone());
|
|
337
|
+
const recorder = new MediaRecorder(encoded, {
|
|
338
|
+
mimeType,
|
|
339
|
+
videoBitsPerSecond: policy.videoBitrate,
|
|
340
|
+
audioBitsPerSecond: policy.audioBitrate,
|
|
341
|
+
});
|
|
342
|
+
let failure = null;
|
|
343
|
+
const finished = new Promise((resolve, reject) => {
|
|
344
|
+
recorder.onstop = () => (failure ? reject(failure) : resolve());
|
|
345
|
+
recorder.onerror = () => {
|
|
346
|
+
failure = new RecordingError("Recording encoder failed");
|
|
347
|
+
if (recorder.state !== "inactive")
|
|
348
|
+
recorder.stop();
|
|
349
|
+
else
|
|
350
|
+
reject(failure);
|
|
351
|
+
};
|
|
352
|
+
});
|
|
353
|
+
const stop = () => {
|
|
354
|
+
if (recorder.state !== "inactive")
|
|
355
|
+
recorder.stop();
|
|
356
|
+
};
|
|
357
|
+
this.stopSegment = stop;
|
|
358
|
+
recorder.ondataavailable = (event) => {
|
|
359
|
+
try {
|
|
360
|
+
uploader.append(event.data);
|
|
361
|
+
if (!uploader.hasCapacity && recorder.state === "recording") {
|
|
362
|
+
recorder.pause();
|
|
363
|
+
void uploader
|
|
364
|
+
.waitForCapacity()
|
|
365
|
+
.then(() => {
|
|
366
|
+
if (!this.stopped && recorder.state === "paused")
|
|
367
|
+
recorder.resume();
|
|
368
|
+
})
|
|
369
|
+
.catch((error) => {
|
|
370
|
+
failure = error;
|
|
371
|
+
stop();
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
failure =
|
|
377
|
+
error instanceof Error
|
|
378
|
+
? error
|
|
379
|
+
: new RecordingError("Recording buffer failed");
|
|
380
|
+
stop();
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
const draw = () => {
|
|
384
|
+
const current = this.getStream();
|
|
385
|
+
const track = current?.getVideoTracks()[0];
|
|
386
|
+
if (!track || track.readyState !== "live" || !track.enabled) {
|
|
387
|
+
this.stopped = true;
|
|
388
|
+
stop();
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
// A camera flip gets a separate file; WebRTC is untouched.
|
|
392
|
+
if (current !== stream) {
|
|
393
|
+
stop();
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
encoded.getAudioTracks().forEach((audio, index) => {
|
|
397
|
+
audio.enabled = stream.getAudioTracks()[index]?.enabled ?? false;
|
|
398
|
+
});
|
|
399
|
+
context.fillStyle = "#000";
|
|
400
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
401
|
+
if (video.readyState >= 2) {
|
|
402
|
+
const scale = Math.min(canvas.width / video.videoWidth, canvas.height / video.videoHeight), w = video.videoWidth * scale, h = video.videoHeight * scale;
|
|
403
|
+
context.drawImage(video, (canvas.width - w) / 2, (canvas.height - h) / 2, w, h);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
recorder.start(1000);
|
|
407
|
+
draw();
|
|
408
|
+
ready();
|
|
409
|
+
timer = setInterval(draw, 1000 / policy.frameRate);
|
|
410
|
+
segmentTimer = setTimeout(stop, uploader.session.segmentSeconds * 1000);
|
|
411
|
+
await finished;
|
|
412
|
+
if (timer)
|
|
413
|
+
clearInterval(timer);
|
|
414
|
+
if (segmentTimer)
|
|
415
|
+
clearTimeout(segmentTimer);
|
|
416
|
+
encoded.getTracks().forEach((track) => track.stop());
|
|
417
|
+
video.pause();
|
|
418
|
+
video.srcObject = null;
|
|
419
|
+
await uploader.seal();
|
|
420
|
+
const index = this.segmentNumber++;
|
|
421
|
+
let finalize;
|
|
422
|
+
finalize = uploader
|
|
423
|
+
.complete()
|
|
424
|
+
.then((file) => {
|
|
425
|
+
this.files.set(index, file);
|
|
426
|
+
try {
|
|
427
|
+
this.options.onRecordingReady?.(file);
|
|
428
|
+
}
|
|
429
|
+
catch { }
|
|
430
|
+
})
|
|
431
|
+
.catch((error) => {
|
|
432
|
+
this.finalFailure =
|
|
433
|
+
error instanceof Error
|
|
434
|
+
? error
|
|
435
|
+
: new RecordingError("Recording validation failed");
|
|
436
|
+
this.stopped = true;
|
|
437
|
+
this.stopSegment?.();
|
|
438
|
+
})
|
|
439
|
+
.finally(() => this.finalizers.delete(finalize));
|
|
440
|
+
this.finalizers.add(finalize);
|
|
441
|
+
// Uploaded files hold no pending media buffers; validate in the
|
|
442
|
+
// background while the next segment records. Bound pending jobs too.
|
|
443
|
+
if (this.finalizers.size >= 2)
|
|
444
|
+
await Promise.race(this.finalizers);
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
await uploader.abort().catch(() => undefined);
|
|
448
|
+
throw error;
|
|
449
|
+
}
|
|
450
|
+
finally {
|
|
451
|
+
if (timer)
|
|
452
|
+
clearInterval(timer);
|
|
453
|
+
if (segmentTimer)
|
|
454
|
+
clearTimeout(segmentTimer);
|
|
455
|
+
this.stopSegment = null;
|
|
456
|
+
encoded?.getTracks().forEach((track) => track.stop());
|
|
457
|
+
if (video) {
|
|
458
|
+
video.pause();
|
|
459
|
+
video.srcObject = null;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
await Promise.all(this.finalizers);
|
|
464
|
+
if (this.finalFailure)
|
|
465
|
+
throw this.finalFailure;
|
|
466
|
+
return [...this.files.entries()]
|
|
467
|
+
.sort((a, b) => a[0] - b[0])
|
|
468
|
+
.map(([, file]) => file);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
//# sourceMappingURL=recording.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recording.js","sourceRoot":"","sources":["../../src/recording.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CameraRecording, type RecordingOptions, type RecordingFile } from './recording.js';
|
|
1
2
|
/**
|
|
2
3
|
* Thin client SDK over the orchestrator's signaling protocol.
|
|
3
4
|
*
|
|
@@ -58,6 +59,8 @@ import {
|
|
|
58
59
|
type VideoSendStats,
|
|
59
60
|
} from './stats.js';
|
|
60
61
|
|
|
62
|
+
export { CameraRecording, RecordingUploader, RecordingError } from './recording.js';
|
|
63
|
+
export type { RecordingOptions, RecordingEvent, RecordingState, RecordingSession, RecordingFile } from './recording.js';
|
|
61
64
|
export { BroadcastPublisher, WhipError, whipEnd, whipOffer } from './broadcast.js';
|
|
62
65
|
export { AudioMixer, MusicSource } from './music.js';
|
|
63
66
|
export type { MusicInput, MusicOptions, TrackSource } from './music.js';
|
|
@@ -324,6 +327,7 @@ export class StreamClient {
|
|
|
324
327
|
private token: string;
|
|
325
328
|
private joinResult: JoinResult | null = null;
|
|
326
329
|
private localStream: MediaStream | null = null;
|
|
330
|
+
private recording: CameraRecording | null = null;
|
|
327
331
|
// A reconnect tears down every transport and producer. Without remembering
|
|
328
332
|
// that this client was publishing -- and what it was publishing with -- a
|
|
329
333
|
// broadcaster that reconnects rejoins silently, which is worse than being
|
|
@@ -599,6 +603,7 @@ export class StreamClient {
|
|
|
599
603
|
}
|
|
600
604
|
|
|
601
605
|
close(): void {
|
|
606
|
+
void this.stopRecording().catch(() => undefined);
|
|
602
607
|
// Only an app closing a live session is 'left'; kicked, room-closed and the
|
|
603
608
|
// others have already reported themselves and set closedByUs.
|
|
604
609
|
const wasOpen = !this.closedByUs;
|
|
@@ -1745,6 +1750,7 @@ export class StreamClient {
|
|
|
1745
1750
|
|
|
1746
1751
|
/** Stop publishing entirely and release the local devices. */
|
|
1747
1752
|
async unpublish(): Promise<void> {
|
|
1753
|
+
void this.stopRecording().catch(error => this.emit('error', error as Error));
|
|
1748
1754
|
// All of it takes effect now; only telling the server is waited for. So
|
|
1749
1755
|
// publish() and unpublish() act in the order they are called: a publish
|
|
1750
1756
|
// called while this still waits is not undone by it, and one already
|
|
@@ -1777,6 +1783,19 @@ export class StreamClient {
|
|
|
1777
1783
|
}
|
|
1778
1784
|
}
|
|
1779
1785
|
|
|
1786
|
+
/** Paid camera recording; project opt-in, quotas and quality are checked by the backend. */
|
|
1787
|
+
async startRecording(options: RecordingOptions): Promise<void> {
|
|
1788
|
+
if (this.recording) throw new Error('Recording is already active');
|
|
1789
|
+
if (!this.localStream || !this.joinResult || !['host', 'cohost'].includes(this.joinResult.role)) throw new Error('Publish your camera before recording');
|
|
1790
|
+
const recording = new CameraRecording(() => this.camEnabled ? this.localStream : null, () => this.token, options);
|
|
1791
|
+
this.recording = recording;
|
|
1792
|
+
try { await recording.start(); } catch (error) { if (this.recording === recording) this.recording = null; throw error; }
|
|
1793
|
+
}
|
|
1794
|
+
async stopRecording(): Promise<RecordingFile[]> {
|
|
1795
|
+
const recording = this.recording; this.recording = null;
|
|
1796
|
+
return recording ? recording.stop() : [];
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1780
1799
|
get local(): MediaStream | null {
|
|
1781
1800
|
return this.localStream;
|
|
1782
1801
|
}
|