livekit-client 1.6.9 → 1.7.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.
Files changed (38) hide show
  1. package/dist/livekit-client.esm.mjs +83 -31
  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/options.d.ts +5 -0
  6. package/dist/src/options.d.ts.map +1 -1
  7. package/dist/src/room/RTCEngine.d.ts +1 -0
  8. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  9. package/dist/src/room/Room.d.ts +2 -1
  10. package/dist/src/room/Room.d.ts.map +1 -1
  11. package/dist/src/room/defaults.d.ts.map +1 -1
  12. package/dist/src/room/events.d.ts +7 -0
  13. package/dist/src/room/events.d.ts.map +1 -1
  14. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  15. package/dist/src/room/track/LocalAudioTrack.d.ts +1 -1
  16. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  17. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  18. package/dist/src/room/track/RemoteVideoTrack.d.ts +1 -0
  19. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  20. package/dist/src/room/track/utils.d.ts.map +1 -1
  21. package/dist/ts4.2/src/options.d.ts +5 -0
  22. package/dist/ts4.2/src/room/RTCEngine.d.ts +1 -0
  23. package/dist/ts4.2/src/room/Room.d.ts +2 -1
  24. package/dist/ts4.2/src/room/events.d.ts +7 -0
  25. package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +1 -1
  26. package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +1 -0
  27. package/package.json +1 -1
  28. package/src/options.ts +6 -0
  29. package/src/room/RTCEngine.ts +25 -16
  30. package/src/room/Room.ts +15 -5
  31. package/src/room/defaults.ts +1 -0
  32. package/src/room/events.ts +8 -0
  33. package/src/room/participant/Participant.ts +0 -3
  34. package/src/room/track/LocalAudioTrack.ts +1 -0
  35. package/src/room/track/RemoteTrackPublication.ts +6 -1
  36. package/src/room/track/RemoteVideoTrack.test.ts +2 -0
  37. package/src/room/track/RemoteVideoTrack.ts +48 -5
  38. package/src/room/track/utils.ts +3 -2
@@ -12916,6 +12916,13 @@ var RoomEvent;
12916
12916
  * args: ([[LocalTrackPublication]], [[LocalParticipant]])
12917
12917
  */
12918
12918
  RoomEvent["LocalTrackUnpublished"] = "localTrackUnpublished";
12919
+ /**
12920
+ * When a local audio track is published the SDK checks whether there is complete silence
12921
+ * on that track and emits the LocalAudioSilenceDetected event in that case.
12922
+ * This allows for applications to show UI informing users that they might have to
12923
+ * reset their audio hardware or check for proper device connectivity.
12924
+ */
12925
+ RoomEvent["LocalAudioSilenceDetected"] = "localAudioSilenceDetected";
12919
12926
  /**
12920
12927
  * Active speakers changed. List of speakers are ordered by their audio level.
12921
12928
  * loudest speakers first. This will include the LocalParticipant too.
@@ -13940,7 +13947,7 @@ var uaParser = {
13940
13947
  })(uaParser, uaParserExports);
13941
13948
  var UAParser = uaParserExports;
13942
13949
 
13943
- var version$1 = "1.6.9";
13950
+ var version$1 = "1.7.1";
13944
13951
 
13945
13952
  const version = version$1;
13946
13953
  const protocolVersion = 9;
@@ -14026,8 +14033,9 @@ async function detectSilence(track) {
14026
14033
  * @internal
14027
14034
  */
14028
14035
  function getNewAudioContext() {
14036
+ const AudioContext =
14029
14037
  // @ts-ignore
14030
- const AudioContext = window.AudioContext || window.webkitAudioContext;
14038
+ typeof window !== 'undefined' && (window.AudioContext || window.webkitAudioContext);
14031
14039
  if (AudioContext) {
14032
14040
  return new AudioContext({
14033
14041
  latencyHint: 'interactive'
@@ -15569,6 +15577,7 @@ class LocalAudioTrack extends LocalTrack {
15569
15577
  }
15570
15578
  this.emit(TrackEvent.AudioSilenceDetected);
15571
15579
  }
15580
+ return trackIsSilent;
15572
15581
  }
15573
15582
  }
15574
15583
 
@@ -16401,7 +16410,8 @@ class RemoteVideoTrack extends RemoteTrack {
16401
16410
  const lastVisibilityChange = this.elementInfos.reduce((prev, info) => Math.max(prev, info.visibilityChangedAt || 0), 0);
16402
16411
  const backgroundPause = ((_b = (_a = this.adaptiveStreamSettings) === null || _a === void 0 ? void 0 : _a.pauseVideoInBackground) !== null && _b !== void 0 ? _b : true // default to true
16403
16412
  ) ? this.isInBackground : false;
16404
- const isVisible = this.elementInfos.some(info => info.visible) && !backgroundPause;
16413
+ const isPiPMode = this.elementInfos.some(info => info.pictureInPicture);
16414
+ const isVisible = this.elementInfos.some(info => info.visible) && !backgroundPause || isPiPMode;
16405
16415
  if (this.lastVisible === isVisible) {
16406
16416
  return;
16407
16417
  }
@@ -16440,6 +16450,12 @@ class RemoteVideoTrack extends RemoteTrack {
16440
16450
  }
16441
16451
  }
16442
16452
  class HTMLElementInfo {
16453
+ get visible() {
16454
+ return this.isPiP || this.isIntersecting;
16455
+ }
16456
+ get pictureInPicture() {
16457
+ return this.isPiP;
16458
+ }
16443
16459
  constructor(element, visible) {
16444
16460
  this.onVisibilityChanged = entry => {
16445
16461
  var _a;
@@ -16448,13 +16464,24 @@ class HTMLElementInfo {
16448
16464
  isIntersecting
16449
16465
  } = entry;
16450
16466
  if (target === this.element) {
16451
- this.visible = isIntersecting;
16467
+ this.isIntersecting = isIntersecting;
16452
16468
  this.visibilityChangedAt = Date.now();
16453
16469
  (_a = this.handleVisibilityChanged) === null || _a === void 0 ? void 0 : _a.call(this);
16454
16470
  }
16455
16471
  };
16472
+ this.onEnterPiP = () => {
16473
+ var _a;
16474
+ this.isPiP = true;
16475
+ (_a = this.handleVisibilityChanged) === null || _a === void 0 ? void 0 : _a.call(this);
16476
+ };
16477
+ this.onLeavePiP = () => {
16478
+ var _a;
16479
+ this.isPiP = false;
16480
+ (_a = this.handleVisibilityChanged) === null || _a === void 0 ? void 0 : _a.call(this);
16481
+ };
16456
16482
  this.element = element;
16457
- this.visible = visible !== null && visible !== void 0 ? visible : isElementInViewport(element);
16483
+ this.isIntersecting = visible !== null && visible !== void 0 ? visible : isElementInViewport(element);
16484
+ this.isPiP = isWeb() && document.pictureInPictureElement === element;
16458
16485
  this.visibilityChangedAt = 0;
16459
16486
  }
16460
16487
  width() {
@@ -16464,6 +16491,9 @@ class HTMLElementInfo {
16464
16491
  return this.element.clientHeight;
16465
16492
  }
16466
16493
  observe() {
16494
+ // make sure we update the current visible state once we start to observe
16495
+ this.isIntersecting = isElementInViewport(this.element);
16496
+ this.isPiP = document.pictureInPictureElement === this.element;
16467
16497
  this.element.handleResize = () => {
16468
16498
  var _a;
16469
16499
  (_a = this.handleResize) === null || _a === void 0 ? void 0 : _a.call(this);
@@ -16471,11 +16501,15 @@ class HTMLElementInfo {
16471
16501
  this.element.handleVisibilityChanged = this.onVisibilityChanged;
16472
16502
  getIntersectionObserver().observe(this.element);
16473
16503
  getResizeObserver().observe(this.element);
16504
+ this.element.addEventListener('enterpictureinpicture', this.onEnterPiP);
16505
+ this.element.addEventListener('leavepictureinpicture', this.onLeavePiP);
16474
16506
  }
16475
16507
  stopObserving() {
16476
16508
  var _a, _b;
16477
16509
  (_a = getIntersectionObserver()) === null || _a === void 0 ? void 0 : _a.unobserve(this.element);
16478
16510
  (_b = getResizeObserver()) === null || _b === void 0 ? void 0 : _b.unobserve(this.element);
16511
+ this.element.removeEventListener('enterpictureinpicture', this.onEnterPiP);
16512
+ this.element.removeEventListener('leavepictureinpicture', this.onLeavePiP);
16479
16513
  }
16480
16514
  }
16481
16515
  // does not account for occlusion by other elements
@@ -16778,9 +16812,6 @@ class Participant extends eventsExports.EventEmitter {
16778
16812
  * @returns
16779
16813
  */
16780
16814
  getTrack(source) {
16781
- if (source === Track.Source.Unknown) {
16782
- return;
16783
- }
16784
16815
  for (const [, pub] of this.tracks) {
16785
16816
  if (pub.source === source) {
16786
16817
  return pub;
@@ -17343,10 +17374,14 @@ class RemoteTrackPublication extends TrackPublication {
17343
17374
  }
17344
17375
  /** @internal */
17345
17376
  updateInfo(info) {
17346
- var _a;
17347
17377
  super.updateInfo(info);
17378
+ const prevMetadataMuted = this.metadataMuted;
17348
17379
  this.metadataMuted = info.muted;
17349
- (_a = this.track) === null || _a === void 0 ? void 0 : _a.setMuted(info.muted);
17380
+ if (this.track) {
17381
+ this.track.setMuted(info.muted);
17382
+ } else if (prevMetadataMuted !== info.muted) {
17383
+ this.emit(info.muted ? TrackEvent.Muted : TrackEvent.Unmuted);
17384
+ }
17350
17385
  }
17351
17386
  emitSubscriptionUpdateIfChanged(previousStatus) {
17352
17387
  const currentStatus = this.subscriptionStatus;
@@ -19113,6 +19148,7 @@ const roomOptionDefaults = {
19113
19148
  dynacast: false,
19114
19149
  stopLocalTrackOnUnpublish: true,
19115
19150
  reconnectPolicy: new DefaultReconnectPolicy(),
19151
+ disconnectOnPageLeave: true,
19116
19152
  expWebAudioMix: false
19117
19153
  };
19118
19154
  const roomConnectOptionDefaults = {
@@ -20105,22 +20141,28 @@ class RTCEngine extends eventsExports.EventEmitter {
20105
20141
  };
20106
20142
  this.handleDataMessage = async message => {
20107
20143
  var _a, _b;
20108
- // decode
20109
- let buffer;
20110
- if (message.data instanceof ArrayBuffer) {
20111
- buffer = message.data;
20112
- } else if (message.data instanceof Blob) {
20113
- buffer = await message.data.arrayBuffer();
20114
- } else {
20115
- livekitLogger.error('unsupported data type', message.data);
20116
- return;
20117
- }
20118
- const dp = DataPacket.decode(new Uint8Array(buffer));
20119
- if (((_a = dp.value) === null || _a === void 0 ? void 0 : _a.$case) === 'speaker') {
20120
- // dispatch speaker updates
20121
- this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.speaker.speakers);
20122
- } else if (((_b = dp.value) === null || _b === void 0 ? void 0 : _b.$case) === 'user') {
20123
- this.emit(EngineEvent.DataPacketReceived, dp.value.user, dp.kind);
20144
+ // make sure to respect incoming data message order by processing message events one after the other
20145
+ const unlock = await this.dataProcessLock.lock();
20146
+ try {
20147
+ // decode
20148
+ let buffer;
20149
+ if (message.data instanceof ArrayBuffer) {
20150
+ buffer = message.data;
20151
+ } else if (message.data instanceof Blob) {
20152
+ buffer = await message.data.arrayBuffer();
20153
+ } else {
20154
+ livekitLogger.error('unsupported data type', message.data);
20155
+ return;
20156
+ }
20157
+ const dp = DataPacket.decode(new Uint8Array(buffer));
20158
+ if (((_a = dp.value) === null || _a === void 0 ? void 0 : _a.$case) === 'speaker') {
20159
+ // dispatch speaker updates
20160
+ this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.speaker.speakers);
20161
+ } else if (((_b = dp.value) === null || _b === void 0 ? void 0 : _b.$case) === 'user') {
20162
+ this.emit(EngineEvent.DataPacketReceived, dp.value.user, dp.kind);
20163
+ }
20164
+ } finally {
20165
+ unlock();
20124
20166
  }
20125
20167
  };
20126
20168
  this.handleDataError = event => {
@@ -20180,6 +20222,7 @@ class RTCEngine extends eventsExports.EventEmitter {
20180
20222
  this.reconnectPolicy = this.options.reconnectPolicy;
20181
20223
  this.registerOnLineListener();
20182
20224
  this.closingLock = new Mutex();
20225
+ this.dataProcessLock = new Mutex();
20183
20226
  }
20184
20227
  async join(url, token, opts, abortSignal) {
20185
20228
  this.url = url;
@@ -21100,8 +21143,10 @@ class Room extends eventsExports.EventEmitter {
21100
21143
  CriticalTimers.clearTimeout(connectTimeout);
21101
21144
  (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.signal.removeEventListener('abort', abortHandler);
21102
21145
  // also hook unload event
21103
- if (isWeb()) {
21104
- window.addEventListener('beforeunload', this.onBeforeUnload);
21146
+ if (isWeb() && this.options.disconnectOnPageLeave) {
21147
+ // capturing both 'pagehide' and 'beforeunload' to capture broadest set of browser behaviors
21148
+ window.addEventListener('pagehide', this.onPageLeave);
21149
+ window.addEventListener('beforeunload', this.onPageLeave);
21105
21150
  (_b = navigator.mediaDevices) === null || _b === void 0 ? void 0 : _b.addEventListener('devicechange', this.handleDeviceChange);
21106
21151
  }
21107
21152
  this.setAndEmitConnectionState(ConnectionState.Connected);
@@ -21152,7 +21197,7 @@ class Room extends eventsExports.EventEmitter {
21152
21197
  unlock();
21153
21198
  }
21154
21199
  };
21155
- this.onBeforeUnload = async () => {
21200
+ this.onPageLeave = async () => {
21156
21201
  await this.disconnect();
21157
21202
  };
21158
21203
  this.handleRestarting = () => {
@@ -21379,8 +21424,14 @@ class Room extends eventsExports.EventEmitter {
21379
21424
  this.onLocalTrackUnmuted = pub => {
21380
21425
  this.emit(RoomEvent.TrackUnmuted, pub, this.localParticipant);
21381
21426
  };
21382
- this.onLocalTrackPublished = pub => {
21427
+ this.onLocalTrackPublished = async pub => {
21383
21428
  this.emit(RoomEvent.LocalTrackPublished, pub, this.localParticipant);
21429
+ if (pub.track instanceof LocalAudioTrack) {
21430
+ const trackIsSilent = await pub.track.checkForSilence();
21431
+ if (trackIsSilent) {
21432
+ this.emit(RoomEvent.LocalAudioSilenceDetected, pub);
21433
+ }
21434
+ }
21384
21435
  };
21385
21436
  this.onLocalTrackUnpublished = pub => {
21386
21437
  this.emit(RoomEvent.LocalTrackUnpublished, pub, this.localParticipant);
@@ -21759,7 +21810,8 @@ class Room extends eventsExports.EventEmitter {
21759
21810
  this.audioContext = undefined;
21760
21811
  }
21761
21812
  if (isWeb()) {
21762
- window.removeEventListener('beforeunload', this.onBeforeUnload);
21813
+ window.removeEventListener('beforeunload', this.onPageLeave);
21814
+ window.removeEventListener('pagehide', this.onPageLeave);
21763
21815
  (_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.removeEventListener('devicechange', this.handleDeviceChange);
21764
21816
  }
21765
21817
  this.setAndEmitConnectionState(ConnectionState.Disconnected);