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.
Files changed (53) hide show
  1. package/dist/livekit-client.esm.mjs +703 -334
  2. package/dist/livekit-client.esm.mjs.map +1 -1
  3. package/dist/livekit-client.umd.js +1 -1
  4. package/dist/livekit-client.umd.js.map +1 -1
  5. package/dist/src/api/SignalClient.d.ts.map +1 -1
  6. package/dist/src/room/PCTransport.d.ts.map +1 -1
  7. package/dist/src/room/RTCEngine.d.ts +12 -4
  8. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  9. package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
  10. package/dist/src/room/Room.d.ts +3 -0
  11. package/dist/src/room/Room.d.ts.map +1 -1
  12. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +4 -0
  13. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
  14. package/dist/src/room/events.d.ts +3 -1
  15. package/dist/src/room/events.d.ts.map +1 -1
  16. package/dist/src/room/participant/LocalParticipant.d.ts +8 -0
  17. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  18. package/dist/src/room/participant/RemoteParticipant.d.ts +4 -3
  19. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  20. package/dist/src/room/track/LocalTrack.d.ts +7 -0
  21. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  22. package/dist/src/room/track/LocalVideoTrack.d.ts +12 -1
  23. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  24. package/dist/src/room/types.d.ts +1 -1
  25. package/dist/src/room/types.d.ts.map +1 -1
  26. package/dist/ts4.2/room/RTCEngine.d.ts +12 -4
  27. package/dist/ts4.2/room/Room.d.ts +3 -0
  28. package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +4 -0
  29. package/dist/ts4.2/room/events.d.ts +3 -1
  30. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +8 -0
  31. package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +4 -3
  32. package/dist/ts4.2/room/track/LocalTrack.d.ts +7 -0
  33. package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +12 -1
  34. package/dist/ts4.2/room/types.d.ts +1 -1
  35. package/package.json +3 -3
  36. package/src/api/SignalClient.ts +4 -0
  37. package/src/room/PCTransport.ts +10 -8
  38. package/src/room/RTCEngine.ts +59 -28
  39. package/src/room/RegionUrlProvider.ts +7 -0
  40. package/src/room/Room.ts +93 -23
  41. package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +331 -16
  42. package/src/room/data-track/incoming/IncomingDataTrackManager.ts +92 -41
  43. package/src/room/events.ts +2 -0
  44. package/src/room/participant/LocalParticipant.ts +70 -5
  45. package/src/room/participant/RemoteParticipant.ts +14 -2
  46. package/src/room/token-source/TokenSource.test.ts +337 -0
  47. package/src/room/token-source/test-tokens.ts +28 -0
  48. package/src/room/token-source/utils.test.ts +12 -20
  49. package/src/room/track/LocalTrack.ts +15 -1
  50. package/src/room/track/LocalVideoTrack.ts +126 -2
  51. package/src/room/track/RemoteVideoTrack.ts +8 -2
  52. package/src/room/types.ts +2 -1
  53. 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 { ScalabilityMode } from '../participant/publishUtils';
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
- this.isPiP = isElementInPiP(this.element);
388
- this.handleVisibilityChanged?.();
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.get(key);
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 'WaitableMap';
46
+ return 'DeferrableMap';
47
47
  }
48
48
 
49
49
  /**