livekit-client 2.13.5 → 2.13.6

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 (41) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +11 -3
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +86 -76
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.umd.js +1 -1
  8. package/dist/livekit-client.umd.js.map +1 -1
  9. package/dist/src/api/SignalClient.d.ts +5 -5
  10. package/dist/src/api/SignalClient.d.ts.map +1 -1
  11. package/dist/src/connectionHelper/checks/publishVideo.d.ts.map +1 -1
  12. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  13. package/dist/src/e2ee/types.d.ts +1 -0
  14. package/dist/src/e2ee/types.d.ts.map +1 -1
  15. package/dist/src/e2ee/worker/FrameCryptor.d.ts +2 -1
  16. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  17. package/dist/src/room/PCTransport.d.ts +3 -2
  18. package/dist/src/room/PCTransport.d.ts.map +1 -1
  19. package/dist/src/room/PCTransportManager.d.ts +3 -3
  20. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  21. package/dist/src/room/RTCEngine.d.ts +4 -0
  22. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  23. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  24. package/dist/ts4.2/src/api/SignalClient.d.ts +5 -5
  25. package/dist/ts4.2/src/e2ee/types.d.ts +1 -0
  26. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +2 -1
  27. package/dist/ts4.2/src/room/PCTransport.d.ts +3 -2
  28. package/dist/ts4.2/src/room/PCTransportManager.d.ts +3 -3
  29. package/dist/ts4.2/src/room/RTCEngine.d.ts +4 -0
  30. package/package.json +8 -8
  31. package/src/api/SignalClient.ts +13 -14
  32. package/src/connectionHelper/checks/publishVideo.ts +1 -0
  33. package/src/e2ee/E2eeManager.ts +3 -0
  34. package/src/e2ee/types.ts +1 -0
  35. package/src/e2ee/worker/FrameCryptor.ts +15 -0
  36. package/src/e2ee/worker/e2ee.worker.ts +2 -0
  37. package/src/room/PCTransport.ts +30 -4
  38. package/src/room/PCTransportManager.ts +10 -7
  39. package/src/room/RTCEngine.ts +15 -7
  40. package/src/room/Room.ts +9 -9
  41. package/src/room/track/LocalVideoTrack.ts +14 -15
@@ -5744,6 +5744,12 @@ const SessionDescription = /* @__PURE__ */proto3.makeMessageType("livekit.Sessio
5744
5744
  kind: "scalar",
5745
5745
  T: 9
5746
5746
  /* ScalarType.STRING */
5747
+ }, {
5748
+ no: 3,
5749
+ name: "id",
5750
+ kind: "scalar",
5751
+ T: 13
5752
+ /* ScalarType.UINT32 */
5747
5753
  }]);
5748
5754
  const ParticipantUpdate = /* @__PURE__ */proto3.makeMessageType("livekit.ParticipantUpdate", () => [{
5749
5755
  no: 1,
@@ -11325,7 +11331,7 @@ function getOSVersion(ua) {
11325
11331
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11326
11332
  }
11327
11333
 
11328
- var version$1 = "2.13.5";
11334
+ var version$1 = "2.13.6";
11329
11335
 
11330
11336
  const version = version$1;
11331
11337
  const protocolVersion = 16;
@@ -12945,7 +12951,8 @@ class E2EEManager extends eventsExports.EventEmitter {
12945
12951
  writableStream: writable,
12946
12952
  trackId: trackId,
12947
12953
  codec,
12948
- participantIdentity: participantIdentity
12954
+ participantIdentity: participantIdentity,
12955
+ isReuse: E2EE_FLAG in receiver
12949
12956
  }
12950
12957
  };
12951
12958
  this.worker.postMessage(msg, [readable, writable]);
@@ -12988,7 +12995,8 @@ class E2EEManager extends eventsExports.EventEmitter {
12988
12995
  writableStream: senderStreams.writable,
12989
12996
  codec,
12990
12997
  trackId,
12991
- participantIdentity: this.room.localParticipant.identity
12998
+ participantIdentity: this.room.localParticipant.identity,
12999
+ isReuse: false
12992
13000
  }
12993
13001
  };
12994
13002
  this.worker.postMessage(msg, [senderStreams.readable, senderStreams.writable]);
@@ -13244,10 +13252,7 @@ class SignalClient {
13244
13252
  sid,
13245
13253
  reconnectReason: reason
13246
13254
  }));
13247
- if (res instanceof ReconnectResponse) {
13248
- return res;
13249
- }
13250
- return;
13255
+ return res;
13251
13256
  });
13252
13257
  }
13253
13258
  connect(url, token, opts, abortSignal) {
@@ -13425,23 +13430,23 @@ class SignalClient {
13425
13430
  });
13426
13431
  }
13427
13432
  // initial offer after joining
13428
- sendOffer(offer) {
13433
+ sendOffer(offer, offerId) {
13429
13434
  this.log.debug('sending offer', Object.assign(Object.assign({}, this.logContext), {
13430
13435
  offerSdp: offer.sdp
13431
13436
  }));
13432
13437
  this.sendRequest({
13433
13438
  case: 'offer',
13434
- value: toProtoSessionDescription(offer)
13439
+ value: toProtoSessionDescription(offer, offerId)
13435
13440
  });
13436
13441
  }
13437
13442
  // answer a server-initiated offer
13438
- sendAnswer(answer) {
13443
+ sendAnswer(answer, offerId) {
13439
13444
  this.log.debug('sending answer', Object.assign(Object.assign({}, this.logContext), {
13440
13445
  answerSdp: answer.sdp
13441
13446
  }));
13442
13447
  return this.sendRequest({
13443
13448
  case: 'answer',
13444
- value: toProtoSessionDescription(answer)
13449
+ value: toProtoSessionDescription(answer, offerId)
13445
13450
  });
13446
13451
  }
13447
13452
  sendIceCandidate(candidate, target) {
@@ -13617,12 +13622,12 @@ class SignalClient {
13617
13622
  if (msg.case === 'answer') {
13618
13623
  const sd = fromProtoSessionDescription(msg.value);
13619
13624
  if (this.onAnswer) {
13620
- this.onAnswer(sd);
13625
+ this.onAnswer(sd, msg.value.id);
13621
13626
  }
13622
13627
  } else if (msg.case === 'offer') {
13623
13628
  const sd = fromProtoSessionDescription(msg.value);
13624
13629
  if (this.onOffer) {
13625
- this.onOffer(sd);
13630
+ this.onOffer(sd, msg.value.id);
13626
13631
  }
13627
13632
  } else if (msg.case === 'trickle') {
13628
13633
  const candidate = JSON.parse(msg.value.candidateInit);
@@ -13793,10 +13798,11 @@ function fromProtoSessionDescription(sd) {
13793
13798
  }
13794
13799
  return rsd;
13795
13800
  }
13796
- function toProtoSessionDescription(rsd) {
13801
+ function toProtoSessionDescription(rsd, id) {
13797
13802
  const sd = new SessionDescription({
13798
13803
  sdp: rsd.sdp,
13799
- type: rsd.type
13804
+ type: rsd.type,
13805
+ id
13800
13806
  });
13801
13807
  return sd;
13802
13808
  }
@@ -14741,6 +14747,7 @@ class PCTransport extends eventsExports.EventEmitter {
14741
14747
  super();
14742
14748
  this.log = livekitLogger;
14743
14749
  this.ddExtID = 0;
14750
+ this.latestOfferId = 0;
14744
14751
  this.pendingCandidates = [];
14745
14752
  this.restartingIce = false;
14746
14753
  this.renegotiate = false;
@@ -14831,9 +14838,16 @@ class PCTransport extends eventsExports.EventEmitter {
14831
14838
  this.pendingCandidates.push(candidate);
14832
14839
  });
14833
14840
  }
14834
- setRemoteDescription(sd) {
14841
+ setRemoteDescription(sd, offerId) {
14835
14842
  return __awaiter(this, void 0, void 0, function* () {
14836
14843
  var _a;
14844
+ if (sd.type === 'answer' && this.latestOfferId > 0 && offerId > 0 && offerId !== this.latestOfferId) {
14845
+ this.log.warn('ignoring answer for old offer', Object.assign(Object.assign({}, this.logContext), {
14846
+ offerId,
14847
+ latestOfferId: this.latestOfferId
14848
+ }));
14849
+ return false;
14850
+ }
14837
14851
  let mungedSDP = undefined;
14838
14852
  if (sd.type === 'offer') {
14839
14853
  let {
@@ -14907,11 +14921,15 @@ class PCTransport extends eventsExports.EventEmitter {
14907
14921
  });
14908
14922
  }
14909
14923
  }
14924
+ return true;
14910
14925
  });
14911
14926
  }
14912
14927
  createAndSendOffer(options) {
14913
14928
  return __awaiter(this, void 0, void 0, function* () {
14914
14929
  var _a;
14930
+ // increase the offer id at the start to ensure the offer is always > 0 so that we can use 0 as a default value for legacy behavior
14931
+ const offerId = this.latestOfferId + 1;
14932
+ this.latestOfferId = offerId;
14915
14933
  if (this.onOffer === undefined) {
14916
14934
  return;
14917
14935
  }
@@ -14986,8 +15004,15 @@ class PCTransport extends eventsExports.EventEmitter {
14986
15004
  });
14987
15005
  }
14988
15006
  });
15007
+ if (this.latestOfferId > offerId) {
15008
+ this.log.warn('latestOfferId mismatch', Object.assign(Object.assign({}, this.logContext), {
15009
+ latestOfferId: this.latestOfferId,
15010
+ offerId
15011
+ }));
15012
+ return;
15013
+ }
14989
15014
  yield this.setMungedSDP(offer, libExports.write(sdpParsed));
14990
- this.onOffer(offer);
15015
+ this.onOffer(offer, this.latestOfferId);
14991
15016
  });
14992
15017
  }
14993
15018
  createAndSetAnswer() {
@@ -15382,9 +15407,9 @@ class PCTransportManager {
15382
15407
  var _a;
15383
15408
  (_a = this.onTrack) === null || _a === void 0 ? void 0 : _a.call(this, ev);
15384
15409
  };
15385
- this.publisher.onOffer = offer => {
15410
+ this.publisher.onOffer = (offer, offerId) => {
15386
15411
  var _a;
15387
- (_a = this.onPublisherOffer) === null || _a === void 0 ? void 0 : _a.call(this, offer);
15412
+ (_a = this.onPublisherOffer) === null || _a === void 0 ? void 0 : _a.call(this, offer, offerId);
15388
15413
  };
15389
15414
  this.state = PCTransportState.NEW;
15390
15415
  this.connectionLock = new _();
@@ -15407,8 +15432,8 @@ class PCTransportManager {
15407
15432
  createAndSendPublisherOffer(options) {
15408
15433
  return this.publisher.createAndSendOffer(options);
15409
15434
  }
15410
- setPublisherAnswer(sd) {
15411
- return this.publisher.setRemoteDescription(sd);
15435
+ setPublisherAnswer(sd, offerId) {
15436
+ return this.publisher.setRemoteDescription(sd, offerId);
15412
15437
  }
15413
15438
  removeTrack(sender) {
15414
15439
  return this.publisher.removeTrack(sender);
@@ -15454,7 +15479,7 @@ class PCTransportManager {
15454
15479
  }
15455
15480
  });
15456
15481
  }
15457
- createSubscriberAnswerFromOffer(sd) {
15482
+ createSubscriberAnswerFromOffer(sd, offerId) {
15458
15483
  return __awaiter(this, void 0, void 0, function* () {
15459
15484
  this.log.debug('received server offer', Object.assign(Object.assign({}, this.logContext), {
15460
15485
  RTCSdpType: sd.type,
@@ -15463,7 +15488,10 @@ class PCTransportManager {
15463
15488
  }));
15464
15489
  const unlock = yield this.remoteOfferLock.lock();
15465
15490
  try {
15466
- yield this.subscriber.setRemoteDescription(sd);
15491
+ const success = yield this.subscriber.setRemoteDescription(sd, offerId);
15492
+ if (!success) {
15493
+ return undefined;
15494
+ }
15467
15495
  // answer the offer
15468
15496
  const answer = yield this.subscriber.createAndSetAnswer();
15469
15497
  return answer;
@@ -17403,44 +17431,13 @@ function setPublishingLayersForSender(sender, senderEncodings, qualities, sender
17403
17431
  return;
17404
17432
  }
17405
17433
  let hasChanged = false;
17406
- const browser = getBrowser();
17407
- const closableSpatial = (browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && compareVersions(browser === null || browser === void 0 ? void 0 : browser.version, '133') > 0;
17434
+ /* disable closable spatial layer as it has video blur / frozen issue with current server / client
17435
+ 1. chrome 113: when switching to up layer with scalability Mode change, it will generate a
17436
+ low resolution frame and recover very quickly, but noticable
17437
+ 2. livekit sfu: additional pli request cause video frozen for a few frames, also noticable */
17438
+ const closableSpatial = false;
17408
17439
  /* @ts-ignore */
17409
- if (closableSpatial && encodings[0].scalabilityMode) {
17410
- // svc dynacast encodings
17411
- const encoding = encodings[0];
17412
- /* @ts-ignore */
17413
- const mode = new ScalabilityMode(encoding.scalabilityMode);
17414
- let maxQuality = VideoQuality$1.OFF;
17415
- qualities.forEach(q => {
17416
- if (q.enabled && (maxQuality === VideoQuality$1.OFF || q.quality > maxQuality)) {
17417
- maxQuality = q.quality;
17418
- }
17419
- });
17420
- if (maxQuality === VideoQuality$1.OFF) {
17421
- if (encoding.active) {
17422
- encoding.active = false;
17423
- hasChanged = true;
17424
- }
17425
- } else if (!encoding.active || mode.spatial !== maxQuality + 1) {
17426
- hasChanged = true;
17427
- encoding.active = true;
17428
- /* @ts-ignore */
17429
- const originalMode = new ScalabilityMode(senderEncodings[0].scalabilityMode);
17430
- mode.spatial = maxQuality + 1;
17431
- mode.suffix = originalMode.suffix;
17432
- if (mode.spatial === 1) {
17433
- // no suffix for L1Tx
17434
- mode.suffix = undefined;
17435
- }
17436
- /* @ts-ignore */
17437
- encoding.scalabilityMode = mode.toString();
17438
- encoding.scaleResolutionDownBy = Math.pow(2, 2 - maxQuality);
17439
- if (senderEncodings[0].maxBitrate) {
17440
- encoding.maxBitrate = senderEncodings[0].maxBitrate / (encoding.scaleResolutionDownBy * encoding.scaleResolutionDownBy);
17441
- }
17442
- }
17443
- } else {
17440
+ if (closableSpatial && encodings[0].scalabilityMode) ; else {
17444
17441
  if (isSVC) {
17445
17442
  const hasEnabledEncoding = qualities.some(q => q.enabled);
17446
17443
  if (hasEnabledEncoding) {
@@ -17577,6 +17574,10 @@ class RTCEngine extends eventsExports.EventEmitter {
17577
17574
  this.rtcConfig = {};
17578
17575
  this.peerConnectionTimeout = roomConnectOptionDefaults.peerConnectionTimeout;
17579
17576
  this.fullReconnectOnNext = false;
17577
+ /**
17578
+ * @internal
17579
+ */
17580
+ this.latestRemoteOfferId = 0;
17580
17581
  this.subscriberPrimary = false;
17581
17582
  this.pcState = PCState.New;
17582
17583
  this._isClosed = true;
@@ -17953,8 +17954,8 @@ class RTCEngine extends eventsExports.EventEmitter {
17953
17954
  this.pcManager.onIceCandidate = (candidate, target) => {
17954
17955
  this.client.sendIceCandidate(candidate, target);
17955
17956
  };
17956
- this.pcManager.onPublisherOffer = offer => {
17957
- this.client.sendOffer(offer);
17957
+ this.pcManager.onPublisherOffer = (offer, offerId) => {
17958
+ this.client.sendOffer(offer, offerId);
17958
17959
  };
17959
17960
  this.pcManager.onDataChannel = this.handleDataChannel;
17960
17961
  this.pcManager.onStateChange = (connectionState, publisherState, subscriberState) => __awaiter(this, void 0, void 0, function* () {
@@ -17993,14 +17994,14 @@ class RTCEngine extends eventsExports.EventEmitter {
17993
17994
  }
17994
17995
  setupSignalClientCallbacks() {
17995
17996
  // configure signaling client
17996
- this.client.onAnswer = sd => __awaiter(this, void 0, void 0, function* () {
17997
+ this.client.onAnswer = (sd, offerId) => __awaiter(this, void 0, void 0, function* () {
17997
17998
  if (!this.pcManager) {
17998
17999
  return;
17999
18000
  }
18000
18001
  this.log.debug('received server answer', Object.assign(Object.assign({}, this.logContext), {
18001
18002
  RTCSdpType: sd.type
18002
18003
  }));
18003
- yield this.pcManager.setPublisherAnswer(sd);
18004
+ yield this.pcManager.setPublisherAnswer(sd, offerId);
18004
18005
  });
18005
18006
  // add candidate on trickle
18006
18007
  this.client.onTrickle = (candidate, target) => {
@@ -18014,12 +18015,15 @@ class RTCEngine extends eventsExports.EventEmitter {
18014
18015
  this.pcManager.addIceCandidate(candidate, target);
18015
18016
  };
18016
18017
  // when server creates an offer for the client
18017
- this.client.onOffer = sd => __awaiter(this, void 0, void 0, function* () {
18018
+ this.client.onOffer = (sd, offerId) => __awaiter(this, void 0, void 0, function* () {
18019
+ this.latestRemoteOfferId = offerId;
18018
18020
  if (!this.pcManager) {
18019
18021
  return;
18020
18022
  }
18021
- const answer = yield this.pcManager.createSubscriberAnswerFromOffer(sd);
18022
- this.client.sendAnswer(answer);
18023
+ const answer = yield this.pcManager.createSubscriberAnswerFromOffer(sd, offerId);
18024
+ if (answer) {
18025
+ this.client.sendAnswer(answer, offerId);
18026
+ }
18023
18027
  });
18024
18028
  this.client.onLocalTrackPublished = res => {
18025
18029
  var _a;
@@ -24061,15 +24065,15 @@ class Room extends eventsExports.EventEmitter {
24061
24065
  var _this3 = this;
24062
24066
  let exact = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
24063
24067
  return function* () {
24064
- var _a, _b, _c, _d, _e, _f, _g;
24065
- var _h;
24068
+ var _a, _b, _c, _d, _e, _f;
24069
+ var _g;
24066
24070
  let success = true;
24067
- let needsUpdateWithoutTracks = false;
24071
+ let shouldTriggerImmediateDeviceChange = false;
24068
24072
  const deviceConstraint = exact ? {
24069
24073
  exact: deviceId
24070
24074
  } : deviceId;
24071
24075
  if (kind === 'audioinput') {
24072
- needsUpdateWithoutTracks = _this3.localParticipant.audioTrackPublications.size === 0;
24076
+ shouldTriggerImmediateDeviceChange = _this3.localParticipant.audioTrackPublications.size === 0;
24073
24077
  const prevDeviceId = (_a = _this3.getActiveDevice(kind)) !== null && _a !== void 0 ? _a : _this3.options.audioCaptureDefaults.deviceId;
24074
24078
  _this3.options.audioCaptureDefaults.deviceId = deviceConstraint;
24075
24079
  const tracks = Array.from(_this3.localParticipant.audioTrackPublications.values()).filter(track => track.source === Track.Source.Microphone);
@@ -24082,8 +24086,13 @@ class Room extends eventsExports.EventEmitter {
24082
24086
  _this3.options.audioCaptureDefaults.deviceId = prevDeviceId;
24083
24087
  throw e;
24084
24088
  }
24089
+ const isMuted = tracks.some(t => {
24090
+ var _a, _b;
24091
+ return (_b = (_a = t.track) === null || _a === void 0 ? void 0 : _a.isMuted) !== null && _b !== void 0 ? _b : false;
24092
+ });
24093
+ if (success && isMuted) shouldTriggerImmediateDeviceChange = true;
24085
24094
  } else if (kind === 'videoinput') {
24086
- needsUpdateWithoutTracks = _this3.localParticipant.videoTrackPublications.size === 0;
24095
+ shouldTriggerImmediateDeviceChange = _this3.localParticipant.videoTrackPublications.size === 0;
24087
24096
  const prevDeviceId = (_b = _this3.getActiveDevice(kind)) !== null && _b !== void 0 ? _b : _this3.options.videoCaptureDefaults.deviceId;
24088
24097
  _this3.options.videoCaptureDefaults.deviceId = deviceConstraint;
24089
24098
  const tracks = Array.from(_this3.localParticipant.videoTrackPublications.values()).filter(track => track.source === Track.Source.Camera);
@@ -24097,6 +24106,7 @@ class Room extends eventsExports.EventEmitter {
24097
24106
  throw e;
24098
24107
  }
24099
24108
  } else if (kind === 'audiooutput') {
24109
+ shouldTriggerImmediateDeviceChange = true;
24100
24110
  if (!supportsSetSinkId() && !_this3.options.webAudioMix || _this3.options.webAudioMix && _this3.audioContext && !('setSinkId' in _this3.audioContext)) {
24101
24111
  throw new Error('cannot switch audio output, setSinkId not supported');
24102
24112
  }
@@ -24104,7 +24114,7 @@ class Room extends eventsExports.EventEmitter {
24104
24114
  // setting `default` for web audio output doesn't work, so we need to normalize the id before
24105
24115
  deviceId = (_c = yield DeviceManager.getInstance().normalizeDeviceId('audiooutput', deviceId)) !== null && _c !== void 0 ? _c : '';
24106
24116
  }
24107
- (_d = (_h = _this3.options).audioOutput) !== null && _d !== void 0 ? _d : _h.audioOutput = {};
24117
+ (_d = (_g = _this3.options).audioOutput) !== null && _d !== void 0 ? _d : _g.audioOutput = {};
24108
24118
  const prevDeviceId = (_e = _this3.getActiveDevice(kind)) !== null && _e !== void 0 ? _e : _this3.options.audioOutput.deviceId;
24109
24119
  _this3.options.audioOutput.deviceId = deviceId;
24110
24120
  try {
@@ -24122,9 +24132,8 @@ class Room extends eventsExports.EventEmitter {
24122
24132
  throw e;
24123
24133
  }
24124
24134
  }
24125
- if (needsUpdateWithoutTracks || kind === 'audiooutput') {
24126
- // if there are not active tracks yet or we're switching audiooutput, we need to manually update the active device map here as changing audio output won't result in a track restart
24127
- _this3.localParticipant.activeDeviceMap.set(kind, kind === 'audiooutput' && ((_g = _this3.options.audioOutput) === null || _g === void 0 ? void 0 : _g.deviceId) || deviceId);
24135
+ if (shouldTriggerImmediateDeviceChange) {
24136
+ _this3.localParticipant.activeDeviceMap.set(kind, deviceId);
24128
24137
  _this3.emit(RoomEvent.ActiveDeviceChanged, kind, deviceId);
24129
24138
  }
24130
24139
  return success;
@@ -25249,6 +25258,7 @@ class PublishVideoCheck extends Checker {
25249
25258
  };
25250
25259
  video.play();
25251
25260
  });
25261
+ stream.getTracks().forEach(t => t.stop());
25252
25262
  video.remove();
25253
25263
  });
25254
25264
  }