livekit-client 2.18.3 → 2.18.5
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/dist/livekit-client.esm.mjs +703 -334
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +12 -4
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +3 -0
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +4 -0
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +3 -1
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +8 -0
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +4 -3
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +7 -0
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +12 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +1 -1
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/ts4.2/room/RTCEngine.d.ts +12 -4
- package/dist/ts4.2/room/Room.d.ts +3 -0
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +4 -0
- package/dist/ts4.2/room/events.d.ts +3 -1
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +8 -0
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +4 -3
- package/dist/ts4.2/room/track/LocalTrack.d.ts +7 -0
- package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +12 -1
- package/dist/ts4.2/room/types.d.ts +1 -1
- package/package.json +3 -3
- package/src/api/SignalClient.ts +4 -0
- package/src/room/PCTransport.ts +10 -8
- package/src/room/RTCEngine.ts +59 -28
- package/src/room/RegionUrlProvider.ts +7 -0
- package/src/room/Room.ts +93 -23
- package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +331 -16
- package/src/room/data-track/incoming/IncomingDataTrackManager.ts +92 -41
- package/src/room/events.ts +2 -0
- package/src/room/participant/LocalParticipant.ts +70 -5
- package/src/room/participant/RemoteParticipant.ts +14 -2
- package/src/room/token-source/TokenSource.test.ts +337 -0
- package/src/room/token-source/test-tokens.ts +28 -0
- package/src/room/token-source/utils.test.ts +12 -20
- package/src/room/track/LocalTrack.ts +15 -1
- package/src/room/track/LocalVideoTrack.ts +126 -2
- package/src/room/track/RemoteVideoTrack.ts +8 -2
- package/src/room/types.ts +2 -1
- package/src/utils/deferrable-map.ts +2 -2
|
@@ -8,14 +8,19 @@ import {
|
|
|
8
8
|
import type { SignalClient } from '../../api/SignalClient';
|
|
9
9
|
import type { StructuredLogger } from '../../logger';
|
|
10
10
|
import { TrackEvent } from '../events';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
ScalabilityMode,
|
|
13
|
+
computeTrackBackupEncodings,
|
|
14
|
+
computeVideoEncodings,
|
|
15
|
+
} from '../participant/publishUtils';
|
|
12
16
|
import type { VideoSenderStats } from '../stats';
|
|
13
17
|
import { computeBitrate, monitorFrequency } from '../stats';
|
|
14
18
|
import type { LoggerOptions } from '../types';
|
|
15
19
|
import { isFireFox, isMobile, isSVCCodec, isWeb } from '../utils';
|
|
16
20
|
import LocalTrack from './LocalTrack';
|
|
17
21
|
import { Track, VideoQuality } from './Track';
|
|
18
|
-
import type { VideoCaptureOptions, VideoCodec } from './options';
|
|
22
|
+
import type { TrackPublishOptions, VideoCaptureOptions, VideoCodec } from './options';
|
|
23
|
+
import { isBackupVideoCodec } from './options';
|
|
19
24
|
import type { TrackProcessor } from './processor/types';
|
|
20
25
|
import { constraintsForOptions } from './utils';
|
|
21
26
|
|
|
@@ -61,6 +66,12 @@ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
|
|
|
61
66
|
|
|
62
67
|
private optimizeForPerformance: boolean = false;
|
|
63
68
|
|
|
69
|
+
/* @internal */
|
|
70
|
+
publishOptions?: TrackPublishOptions;
|
|
71
|
+
|
|
72
|
+
/* @internal */
|
|
73
|
+
lastEncodedDimensions?: Track.Dimensions;
|
|
74
|
+
|
|
64
75
|
get sender(): RTCRtpSender | undefined {
|
|
65
76
|
return this._sender;
|
|
66
77
|
}
|
|
@@ -265,6 +276,119 @@ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
|
|
|
265
276
|
await sc.sender.replaceTrack(sc.mediaStreamTrack);
|
|
266
277
|
}
|
|
267
278
|
}
|
|
279
|
+
|
|
280
|
+
// The new MediaStreamTrack may have different dimensions than the previous one
|
|
281
|
+
// (e.g. switching between cameras with different native resolutions), which would
|
|
282
|
+
// leave the sender's encoding parameters (scaleResolutionDownBy, maxBitrate, etc.)
|
|
283
|
+
// based on the old dimensions. Recompute them so the encoded output matches the
|
|
284
|
+
// new source.
|
|
285
|
+
await this.onSenderTrackSwapped();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
protected override async onSenderTrackSwapped(): Promise<void> {
|
|
289
|
+
await this.refreshSenderEncodings();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Recomputes encoding parameters for this track's senders based on the current
|
|
294
|
+
* MediaStreamTrack dimensions and reapplies them via setParameters. This is a no-op
|
|
295
|
+
* if the track hasn't been published yet or if the track is in performance-optimized
|
|
296
|
+
* mode (which manages its own encodings).
|
|
297
|
+
*/
|
|
298
|
+
private async refreshSenderEncodings() {
|
|
299
|
+
if (!this.sender || !this.publishOptions || this.optimizeForPerformance) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const unlock = await this.senderLock.lock();
|
|
303
|
+
try {
|
|
304
|
+
let dims: Track.Dimensions;
|
|
305
|
+
try {
|
|
306
|
+
dims = await this.waitForDimensions();
|
|
307
|
+
} catch (e) {
|
|
308
|
+
this.log.warn('could not determine new track dimensions, skipping encoding recompute', {
|
|
309
|
+
...this.logContext,
|
|
310
|
+
error: e,
|
|
311
|
+
});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (
|
|
316
|
+
this.lastEncodedDimensions &&
|
|
317
|
+
this.lastEncodedDimensions.width === dims.width &&
|
|
318
|
+
this.lastEncodedDimensions.height === dims.height
|
|
319
|
+
) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const isScreenShare = this.source === Track.Source.ScreenShare;
|
|
324
|
+
const newEncodings = computeVideoEncodings(isScreenShare, dims.width, dims.height, {
|
|
325
|
+
...this.publishOptions,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
await this.applyEncodingsToSender(this.sender, newEncodings);
|
|
329
|
+
this.encodings = newEncodings;
|
|
330
|
+
this.lastEncodedDimensions = dims;
|
|
331
|
+
|
|
332
|
+
for (const [codec, sc] of this.simulcastCodecs) {
|
|
333
|
+
if (!sc.sender || sc.sender.transport?.state === 'closed') {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (!isBackupVideoCodec(codec)) {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const backupOpts: TrackPublishOptions = { ...this.publishOptions };
|
|
340
|
+
const backupEncodings = computeTrackBackupEncodings(this, codec, backupOpts);
|
|
341
|
+
if (!backupEncodings) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
await this.applyEncodingsToSender(sc.sender, backupEncodings);
|
|
345
|
+
sc.encodings = backupEncodings;
|
|
346
|
+
}
|
|
347
|
+
} catch (e) {
|
|
348
|
+
this.log.warn('failed to apply recomputed encodings', {
|
|
349
|
+
...this.logContext,
|
|
350
|
+
error: e,
|
|
351
|
+
});
|
|
352
|
+
} finally {
|
|
353
|
+
unlock();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private async applyEncodingsToSender(
|
|
358
|
+
sender: RTCRtpSender,
|
|
359
|
+
encodings: RTCRtpEncodingParameters[],
|
|
360
|
+
) {
|
|
361
|
+
const params = sender.getParameters();
|
|
362
|
+
if (!params.encodings || params.encodings.length !== encodings.length) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
params.encodings.forEach((existing, idx) => {
|
|
366
|
+
// preserve disabled layers (dynacast / Firefox workaround in
|
|
367
|
+
// setPublishingLayersForSender set scaleResolutionDownBy/maxBitrate to sentinel
|
|
368
|
+
// values for disabled layers — don't clobber those).
|
|
369
|
+
if (existing.active === false) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const next = encodings[idx];
|
|
373
|
+
if (next.scaleResolutionDownBy !== undefined) {
|
|
374
|
+
existing.scaleResolutionDownBy = next.scaleResolutionDownBy;
|
|
375
|
+
}
|
|
376
|
+
if (next.maxBitrate !== undefined) {
|
|
377
|
+
existing.maxBitrate = next.maxBitrate;
|
|
378
|
+
}
|
|
379
|
+
if (next.maxFramerate !== undefined) {
|
|
380
|
+
existing.maxFramerate = next.maxFramerate;
|
|
381
|
+
}
|
|
382
|
+
if (next.priority !== undefined) {
|
|
383
|
+
existing.priority = next.priority;
|
|
384
|
+
existing.networkPriority = next.priority;
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
this.log.debug('updating sender encodings after track restart', {
|
|
388
|
+
...this.logContext,
|
|
389
|
+
encodings: params.encodings,
|
|
390
|
+
});
|
|
391
|
+
await sender.setParameters(params);
|
|
268
392
|
}
|
|
269
393
|
|
|
270
394
|
async setProcessor(
|
|
@@ -384,8 +384,14 @@ class HTMLElementInfo implements ElementInfo {
|
|
|
384
384
|
|
|
385
385
|
private onEnterPiP = () => {
|
|
386
386
|
window.documentPictureInPicture?.window?.addEventListener('pagehide', this.onLeavePiP);
|
|
387
|
-
|
|
388
|
-
|
|
387
|
+
// Document PiP: the browser may fire 'enter' before the app has appended its subtree into
|
|
388
|
+
// documentPictureInPicture.window. Defer so pipWin.document.contains(video) is reliable.
|
|
389
|
+
queueMicrotask(() => {
|
|
390
|
+
requestAnimationFrame(() => {
|
|
391
|
+
this.isPiP = isElementInPiP(this.element);
|
|
392
|
+
this.handleVisibilityChanged?.();
|
|
393
|
+
});
|
|
394
|
+
});
|
|
389
395
|
};
|
|
390
396
|
|
|
391
397
|
private onLeavePiP = () => {
|
package/src/room/types.ts
CHANGED
|
@@ -92,7 +92,8 @@ export type SimulationScenario =
|
|
|
92
92
|
| 'disconnect-signal-on-resume'
|
|
93
93
|
| 'disconnect-signal-on-resume-no-messages'
|
|
94
94
|
// instructs the server to send a full reconnect reconnect action to the client
|
|
95
|
-
| 'leave-full-reconnect'
|
|
95
|
+
| 'leave-full-reconnect'
|
|
96
|
+
| 'fail-on-v1-path';
|
|
96
97
|
|
|
97
98
|
export type LoggerOptions = {
|
|
98
99
|
loggerName?: string;
|
|
@@ -29,7 +29,7 @@ export class DeferrableMap<K, V> extends Map<K, V> {
|
|
|
29
29
|
super.set(key, value);
|
|
30
30
|
|
|
31
31
|
// Resolve any futures waiting on this key.
|
|
32
|
-
const futures = this.pending
|
|
32
|
+
const futures = this.pending?.get(key);
|
|
33
33
|
if (futures) {
|
|
34
34
|
for (const future of futures) {
|
|
35
35
|
if (!future.isResolved) {
|
|
@@ -43,7 +43,7 @@ export class DeferrableMap<K, V> extends Map<K, V> {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
get [Symbol.toStringTag](): string {
|
|
46
|
-
return '
|
|
46
|
+
return 'DeferrableMap';
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|