livekit-client 2.13.4 → 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 (52) 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 +329 -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 +8 -0
  22. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  23. package/dist/src/room/Room.d.ts +1 -1
  24. package/dist/src/room/Room.d.ts.map +1 -1
  25. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  26. package/dist/src/utils/dataPacketBuffer.d.ts +15 -0
  27. package/dist/src/utils/dataPacketBuffer.d.ts.map +1 -0
  28. package/dist/src/utils/ttlmap.d.ts +20 -0
  29. package/dist/src/utils/ttlmap.d.ts.map +1 -0
  30. package/dist/ts4.2/src/api/SignalClient.d.ts +5 -5
  31. package/dist/ts4.2/src/e2ee/types.d.ts +1 -0
  32. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +2 -1
  33. package/dist/ts4.2/src/room/PCTransport.d.ts +3 -2
  34. package/dist/ts4.2/src/room/PCTransportManager.d.ts +3 -3
  35. package/dist/ts4.2/src/room/RTCEngine.d.ts +8 -0
  36. package/dist/ts4.2/src/room/Room.d.ts +1 -1
  37. package/dist/ts4.2/src/utils/dataPacketBuffer.d.ts +15 -0
  38. package/dist/ts4.2/src/utils/ttlmap.d.ts +20 -0
  39. package/package.json +8 -8
  40. package/src/api/SignalClient.ts +12 -10
  41. package/src/connectionHelper/checks/publishVideo.ts +1 -0
  42. package/src/e2ee/E2eeManager.ts +3 -0
  43. package/src/e2ee/types.ts +1 -0
  44. package/src/e2ee/worker/FrameCryptor.ts +15 -0
  45. package/src/e2ee/worker/e2ee.worker.ts +2 -0
  46. package/src/room/PCTransport.ts +30 -4
  47. package/src/room/PCTransportManager.ts +10 -7
  48. package/src/room/RTCEngine.ts +78 -9
  49. package/src/room/Room.ts +11 -11
  50. package/src/room/track/LocalVideoTrack.ts +14 -15
  51. package/src/utils/dataPacketBuffer.ts +52 -0
  52. package/src/utils/ttlmap.ts +96 -0
@@ -2966,7 +2966,17 @@ function makeUtilCommon() {
2966
2966
  }
2967
2967
  switch (m.kind) {
2968
2968
  case "message":
2969
- return m.T.equals(va, vb);
2969
+ let a = va;
2970
+ let b = vb;
2971
+ if (m.T.fieldWrapper) {
2972
+ if (a !== undefined && !isMessage(a)) {
2973
+ a = m.T.fieldWrapper.wrapField(a);
2974
+ }
2975
+ if (b !== undefined && !isMessage(b)) {
2976
+ b = m.T.fieldWrapper.wrapField(b);
2977
+ }
2978
+ }
2979
+ return m.T.equals(a, b);
2970
2980
  case "enum":
2971
2981
  return scalarEquals(ScalarType.INT32, va, vb);
2972
2982
  case "scalar":
@@ -3887,6 +3897,9 @@ const DisconnectReason = /* @__PURE__ */proto3.makeEnum("livekit.DisconnectReaso
3887
3897
  }, {
3888
3898
  no: 14,
3889
3899
  name: "CONNECTION_TIMEOUT"
3900
+ }, {
3901
+ no: 15,
3902
+ name: "MEDIA_FAILURE"
3890
3903
  }]);
3891
3904
  const ReconnectReason = /* @__PURE__ */proto3.makeEnum("livekit.ReconnectReason", [{
3892
3905
  no: 0,
@@ -4491,6 +4504,18 @@ const DataPacket = /* @__PURE__ */proto3.makeMessageType("livekit.DataPacket", (
4491
4504
  kind: "message",
4492
4505
  T: DataStream_Trailer,
4493
4506
  oneof: "value"
4507
+ }, {
4508
+ no: 16,
4509
+ name: "sequence",
4510
+ kind: "scalar",
4511
+ T: 13
4512
+ /* ScalarType.UINT32 */
4513
+ }, {
4514
+ no: 17,
4515
+ name: "participant_sid",
4516
+ kind: "scalar",
4517
+ T: 9
4518
+ /* ScalarType.STRING */
4494
4519
  }]);
4495
4520
  const DataPacket_Kind = /* @__PURE__ */proto3.makeEnum("livekit.DataPacket.Kind", [{
4496
4521
  no: 0,
@@ -5676,6 +5701,17 @@ const ReconnectResponse = /* @__PURE__ */proto3.makeMessageType("livekit.Reconne
5676
5701
  name: "client_configuration",
5677
5702
  kind: "message",
5678
5703
  T: ClientConfiguration
5704
+ }, {
5705
+ no: 3,
5706
+ name: "server_info",
5707
+ kind: "message",
5708
+ T: ServerInfo
5709
+ }, {
5710
+ no: 4,
5711
+ name: "last_message_seq",
5712
+ kind: "scalar",
5713
+ T: 13
5714
+ /* ScalarType.UINT32 */
5679
5715
  }]);
5680
5716
  const TrackPublishedResponse = /* @__PURE__ */proto3.makeMessageType("livekit.TrackPublishedResponse", () => [{
5681
5717
  no: 1,
@@ -5708,6 +5744,12 @@ const SessionDescription = /* @__PURE__ */proto3.makeMessageType("livekit.Sessio
5708
5744
  kind: "scalar",
5709
5745
  T: 9
5710
5746
  /* ScalarType.STRING */
5747
+ }, {
5748
+ no: 3,
5749
+ name: "id",
5750
+ kind: "scalar",
5751
+ T: 13
5752
+ /* ScalarType.UINT32 */
5711
5753
  }]);
5712
5754
  const ParticipantUpdate = /* @__PURE__ */proto3.makeMessageType("livekit.ParticipantUpdate", () => [{
5713
5755
  no: 1,
@@ -6122,6 +6164,25 @@ const SyncState = /* @__PURE__ */proto3.makeMessageType("livekit.SyncState", ()
6122
6164
  kind: "scalar",
6123
6165
  T: 9,
6124
6166
  repeated: true
6167
+ }, {
6168
+ no: 7,
6169
+ name: "datachannel_receive_states",
6170
+ kind: "message",
6171
+ T: DataChannelReceiveState,
6172
+ repeated: true
6173
+ }]);
6174
+ const DataChannelReceiveState = /* @__PURE__ */proto3.makeMessageType("livekit.DataChannelReceiveState", () => [{
6175
+ no: 1,
6176
+ name: "publisher_sid",
6177
+ kind: "scalar",
6178
+ T: 9
6179
+ /* ScalarType.STRING */
6180
+ }, {
6181
+ no: 2,
6182
+ name: "last_seq",
6183
+ kind: "scalar",
6184
+ T: 13
6185
+ /* ScalarType.UINT32 */
6125
6186
  }]);
6126
6187
  const DataChannelInfo = /* @__PURE__ */proto3.makeMessageType("livekit.DataChannelInfo", () => [{
6127
6188
  no: 1,
@@ -11270,7 +11331,7 @@ function getOSVersion(ua) {
11270
11331
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11271
11332
  }
11272
11333
 
11273
- var version$1 = "2.13.4";
11334
+ var version$1 = "2.13.6";
11274
11335
 
11275
11336
  const version = version$1;
11276
11337
  const protocolVersion = 16;
@@ -12890,7 +12951,8 @@ class E2EEManager extends eventsExports.EventEmitter {
12890
12951
  writableStream: writable,
12891
12952
  trackId: trackId,
12892
12953
  codec,
12893
- participantIdentity: participantIdentity
12954
+ participantIdentity: participantIdentity,
12955
+ isReuse: E2EE_FLAG in receiver
12894
12956
  }
12895
12957
  };
12896
12958
  this.worker.postMessage(msg, [readable, writable]);
@@ -12933,7 +12995,8 @@ class E2EEManager extends eventsExports.EventEmitter {
12933
12995
  writableStream: senderStreams.writable,
12934
12996
  codec,
12935
12997
  trackId,
12936
- participantIdentity: this.room.localParticipant.identity
12998
+ participantIdentity: this.room.localParticipant.identity,
12999
+ isReuse: false
12937
13000
  }
12938
13001
  };
12939
13002
  this.worker.postMessage(msg, [senderStreams.readable, senderStreams.writable]);
@@ -13367,23 +13430,23 @@ class SignalClient {
13367
13430
  });
13368
13431
  }
13369
13432
  // initial offer after joining
13370
- sendOffer(offer) {
13433
+ sendOffer(offer, offerId) {
13371
13434
  this.log.debug('sending offer', Object.assign(Object.assign({}, this.logContext), {
13372
13435
  offerSdp: offer.sdp
13373
13436
  }));
13374
13437
  this.sendRequest({
13375
13438
  case: 'offer',
13376
- value: toProtoSessionDescription(offer)
13439
+ value: toProtoSessionDescription(offer, offerId)
13377
13440
  });
13378
13441
  }
13379
13442
  // answer a server-initiated offer
13380
- sendAnswer(answer) {
13443
+ sendAnswer(answer, offerId) {
13381
13444
  this.log.debug('sending answer', Object.assign(Object.assign({}, this.logContext), {
13382
13445
  answerSdp: answer.sdp
13383
13446
  }));
13384
13447
  return this.sendRequest({
13385
13448
  case: 'answer',
13386
- value: toProtoSessionDescription(answer)
13449
+ value: toProtoSessionDescription(answer, offerId)
13387
13450
  });
13388
13451
  }
13389
13452
  sendIceCandidate(candidate, target) {
@@ -13559,12 +13622,12 @@ class SignalClient {
13559
13622
  if (msg.case === 'answer') {
13560
13623
  const sd = fromProtoSessionDescription(msg.value);
13561
13624
  if (this.onAnswer) {
13562
- this.onAnswer(sd);
13625
+ this.onAnswer(sd, msg.value.id);
13563
13626
  }
13564
13627
  } else if (msg.case === 'offer') {
13565
13628
  const sd = fromProtoSessionDescription(msg.value);
13566
13629
  if (this.onOffer) {
13567
- this.onOffer(sd);
13630
+ this.onOffer(sd, msg.value.id);
13568
13631
  }
13569
13632
  } else if (msg.case === 'trickle') {
13570
13633
  const candidate = JSON.parse(msg.value.candidateInit);
@@ -13735,10 +13798,11 @@ function fromProtoSessionDescription(sd) {
13735
13798
  }
13736
13799
  return rsd;
13737
13800
  }
13738
- function toProtoSessionDescription(rsd) {
13801
+ function toProtoSessionDescription(rsd, id) {
13739
13802
  const sd = new SessionDescription({
13740
13803
  sdp: rsd.sdp,
13741
- type: rsd.type
13804
+ type: rsd.type,
13805
+ id
13742
13806
  });
13743
13807
  return sd;
13744
13808
  }
@@ -13787,6 +13851,135 @@ function createConnectionParams(token, info, opts) {
13787
13851
  return params;
13788
13852
  }
13789
13853
 
13854
+ class DataPacketBuffer {
13855
+ constructor() {
13856
+ this.buffer = [];
13857
+ this._totalSize = 0;
13858
+ }
13859
+ push(item) {
13860
+ this.buffer.push(item);
13861
+ this._totalSize += item.data.byteLength;
13862
+ }
13863
+ pop() {
13864
+ const item = this.buffer.shift();
13865
+ if (item) {
13866
+ this._totalSize -= item.data.byteLength;
13867
+ }
13868
+ return item;
13869
+ }
13870
+ getAll() {
13871
+ return this.buffer.slice();
13872
+ }
13873
+ popToSequence(sequence) {
13874
+ while (this.buffer.length > 0) {
13875
+ const first = this.buffer[0];
13876
+ if (first.sequence <= sequence) {
13877
+ this.pop();
13878
+ } else {
13879
+ break;
13880
+ }
13881
+ }
13882
+ }
13883
+ alignBufferedAmount(bufferedAmount) {
13884
+ while (this.buffer.length > 0) {
13885
+ const first = this.buffer[0];
13886
+ if (this._totalSize - first.data.byteLength <= bufferedAmount) {
13887
+ break;
13888
+ }
13889
+ this.pop();
13890
+ }
13891
+ }
13892
+ get length() {
13893
+ return this.buffer.length;
13894
+ }
13895
+ }
13896
+
13897
+ class TTLMap {
13898
+ /**
13899
+ * @param ttl ttl of the key (ms)
13900
+ */
13901
+ constructor(ttl) {
13902
+ this._map = new Map();
13903
+ this._lastCleanup = 0;
13904
+ this.ttl = ttl;
13905
+ }
13906
+ set(key, value) {
13907
+ const now = Date.now();
13908
+ if (now - this._lastCleanup > this.ttl / 2) {
13909
+ this.cleanup();
13910
+ }
13911
+ const expiresAt = now + this.ttl;
13912
+ this._map.set(key, {
13913
+ value,
13914
+ expiresAt
13915
+ });
13916
+ return this;
13917
+ }
13918
+ get(key) {
13919
+ const entry = this._map.get(key);
13920
+ if (!entry) return undefined;
13921
+ if (entry.expiresAt < Date.now()) {
13922
+ this._map.delete(key);
13923
+ return undefined;
13924
+ }
13925
+ return entry.value;
13926
+ }
13927
+ has(key) {
13928
+ const entry = this._map.get(key);
13929
+ if (!entry) return false;
13930
+ if (entry.expiresAt < Date.now()) {
13931
+ this._map.delete(key);
13932
+ return false;
13933
+ }
13934
+ return true;
13935
+ }
13936
+ delete(key) {
13937
+ return this._map.delete(key);
13938
+ }
13939
+ clear() {
13940
+ this._map.clear();
13941
+ }
13942
+ cleanup() {
13943
+ const now = Date.now();
13944
+ for (const [key, entry] of this._map.entries()) {
13945
+ if (entry.expiresAt < now) {
13946
+ this._map.delete(key);
13947
+ }
13948
+ }
13949
+ this._lastCleanup = now;
13950
+ }
13951
+ get size() {
13952
+ this.cleanup();
13953
+ return this._map.size;
13954
+ }
13955
+ forEach(callback) {
13956
+ this.cleanup();
13957
+ for (const [key, entry] of this._map.entries()) {
13958
+ if (entry.expiresAt >= Date.now()) {
13959
+ callback(entry.value, key, this.asValueMap());
13960
+ }
13961
+ }
13962
+ }
13963
+ map(callback) {
13964
+ this.cleanup();
13965
+ const result = [];
13966
+ const valueMap = this.asValueMap();
13967
+ for (const [key, value] of valueMap.entries()) {
13968
+ result.push(callback(value, key, valueMap));
13969
+ }
13970
+ return result;
13971
+ }
13972
+ asValueMap() {
13973
+ const result = new Map();
13974
+ for (const [key, entry] of this._map.entries()) {
13975
+ if (entry.expiresAt >= Date.now()) {
13976
+ result.set(key, entry.value);
13977
+ }
13978
+ }
13979
+ return result;
13980
+ }
13981
+ }
13982
+
13790
13983
  var lib = {};
13791
13984
 
13792
13985
  var parser = {};
@@ -14554,6 +14747,7 @@ class PCTransport extends eventsExports.EventEmitter {
14554
14747
  super();
14555
14748
  this.log = livekitLogger;
14556
14749
  this.ddExtID = 0;
14750
+ this.latestOfferId = 0;
14557
14751
  this.pendingCandidates = [];
14558
14752
  this.restartingIce = false;
14559
14753
  this.renegotiate = false;
@@ -14644,9 +14838,16 @@ class PCTransport extends eventsExports.EventEmitter {
14644
14838
  this.pendingCandidates.push(candidate);
14645
14839
  });
14646
14840
  }
14647
- setRemoteDescription(sd) {
14841
+ setRemoteDescription(sd, offerId) {
14648
14842
  return __awaiter(this, void 0, void 0, function* () {
14649
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
+ }
14650
14851
  let mungedSDP = undefined;
14651
14852
  if (sd.type === 'offer') {
14652
14853
  let {
@@ -14720,11 +14921,15 @@ class PCTransport extends eventsExports.EventEmitter {
14720
14921
  });
14721
14922
  }
14722
14923
  }
14924
+ return true;
14723
14925
  });
14724
14926
  }
14725
14927
  createAndSendOffer(options) {
14726
14928
  return __awaiter(this, void 0, void 0, function* () {
14727
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;
14728
14933
  if (this.onOffer === undefined) {
14729
14934
  return;
14730
14935
  }
@@ -14799,8 +15004,15 @@ class PCTransport extends eventsExports.EventEmitter {
14799
15004
  });
14800
15005
  }
14801
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
+ }
14802
15014
  yield this.setMungedSDP(offer, libExports.write(sdpParsed));
14803
- this.onOffer(offer);
15015
+ this.onOffer(offer, this.latestOfferId);
14804
15016
  });
14805
15017
  }
14806
15018
  createAndSetAnswer() {
@@ -15195,9 +15407,9 @@ class PCTransportManager {
15195
15407
  var _a;
15196
15408
  (_a = this.onTrack) === null || _a === void 0 ? void 0 : _a.call(this, ev);
15197
15409
  };
15198
- this.publisher.onOffer = offer => {
15410
+ this.publisher.onOffer = (offer, offerId) => {
15199
15411
  var _a;
15200
- (_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);
15201
15413
  };
15202
15414
  this.state = PCTransportState.NEW;
15203
15415
  this.connectionLock = new _();
@@ -15220,8 +15432,8 @@ class PCTransportManager {
15220
15432
  createAndSendPublisherOffer(options) {
15221
15433
  return this.publisher.createAndSendOffer(options);
15222
15434
  }
15223
- setPublisherAnswer(sd) {
15224
- return this.publisher.setRemoteDescription(sd);
15435
+ setPublisherAnswer(sd, offerId) {
15436
+ return this.publisher.setRemoteDescription(sd, offerId);
15225
15437
  }
15226
15438
  removeTrack(sender) {
15227
15439
  return this.publisher.removeTrack(sender);
@@ -15267,7 +15479,7 @@ class PCTransportManager {
15267
15479
  }
15268
15480
  });
15269
15481
  }
15270
- createSubscriberAnswerFromOffer(sd) {
15482
+ createSubscriberAnswerFromOffer(sd, offerId) {
15271
15483
  return __awaiter(this, void 0, void 0, function* () {
15272
15484
  this.log.debug('received server offer', Object.assign(Object.assign({}, this.logContext), {
15273
15485
  RTCSdpType: sd.type,
@@ -15276,7 +15488,10 @@ class PCTransportManager {
15276
15488
  }));
15277
15489
  const unlock = yield this.remoteOfferLock.lock();
15278
15490
  try {
15279
- yield this.subscriber.setRemoteDescription(sd);
15491
+ const success = yield this.subscriber.setRemoteDescription(sd, offerId);
15492
+ if (!success) {
15493
+ return undefined;
15494
+ }
15280
15495
  // answer the offer
15281
15496
  const answer = yield this.subscriber.createAndSetAnswer();
15282
15497
  return answer;
@@ -17216,44 +17431,13 @@ function setPublishingLayersForSender(sender, senderEncodings, qualities, sender
17216
17431
  return;
17217
17432
  }
17218
17433
  let hasChanged = false;
17219
- const browser = getBrowser();
17220
- 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;
17221
17439
  /* @ts-ignore */
17222
- if (closableSpatial && encodings[0].scalabilityMode) {
17223
- // svc dynacast encodings
17224
- const encoding = encodings[0];
17225
- /* @ts-ignore */
17226
- const mode = new ScalabilityMode(encoding.scalabilityMode);
17227
- let maxQuality = VideoQuality$1.OFF;
17228
- qualities.forEach(q => {
17229
- if (q.enabled && (maxQuality === VideoQuality$1.OFF || q.quality > maxQuality)) {
17230
- maxQuality = q.quality;
17231
- }
17232
- });
17233
- if (maxQuality === VideoQuality$1.OFF) {
17234
- if (encoding.active) {
17235
- encoding.active = false;
17236
- hasChanged = true;
17237
- }
17238
- } else if (!encoding.active || mode.spatial !== maxQuality + 1) {
17239
- hasChanged = true;
17240
- encoding.active = true;
17241
- /* @ts-ignore */
17242
- const originalMode = new ScalabilityMode(senderEncodings[0].scalabilityMode);
17243
- mode.spatial = maxQuality + 1;
17244
- mode.suffix = originalMode.suffix;
17245
- if (mode.spatial === 1) {
17246
- // no suffix for L1Tx
17247
- mode.suffix = undefined;
17248
- }
17249
- /* @ts-ignore */
17250
- encoding.scalabilityMode = mode.toString();
17251
- encoding.scaleResolutionDownBy = Math.pow(2, 2 - maxQuality);
17252
- if (senderEncodings[0].maxBitrate) {
17253
- encoding.maxBitrate = senderEncodings[0].maxBitrate / (encoding.scaleResolutionDownBy * encoding.scaleResolutionDownBy);
17254
- }
17255
- }
17256
- } else {
17440
+ if (closableSpatial && encodings[0].scalabilityMode) ; else {
17257
17441
  if (isSVC) {
17258
17442
  const hasEnabledEncoding = qualities.some(q => q.enabled);
17259
17443
  if (hasEnabledEncoding) {
@@ -17366,6 +17550,7 @@ const lossyDataChannel = '_lossy';
17366
17550
  const reliableDataChannel = '_reliable';
17367
17551
  const minReconnectWait = 2 * 1000;
17368
17552
  const leaveReconnect = 'leave-reconnect';
17553
+ const reliabeReceiveStateTTL = 30000;
17369
17554
  var PCState;
17370
17555
  (function (PCState) {
17371
17556
  PCState[PCState["New"] = 0] = "New";
@@ -17389,6 +17574,10 @@ class RTCEngine extends eventsExports.EventEmitter {
17389
17574
  this.rtcConfig = {};
17390
17575
  this.peerConnectionTimeout = roomConnectOptionDefaults.peerConnectionTimeout;
17391
17576
  this.fullReconnectOnNext = false;
17577
+ /**
17578
+ * @internal
17579
+ */
17580
+ this.latestRemoteOfferId = 0;
17392
17581
  this.subscriberPrimary = false;
17393
17582
  this.pcState = PCState.New;
17394
17583
  this._isClosed = true;
@@ -17402,6 +17591,9 @@ class RTCEngine extends eventsExports.EventEmitter {
17402
17591
  this.maxJoinAttempts = 1;
17403
17592
  this.shouldFailNext = false;
17404
17593
  this.log = livekitLogger;
17594
+ this.reliableDataSequence = 1;
17595
+ this.reliableMessageBuffer = new DataPacketBuffer();
17596
+ this.reliableReceivedState = new TTLMap(reliabeReceiveStateTTL);
17405
17597
  this.handleDataChannel = _a => __awaiter(this, [_a], void 0, function (_ref) {
17406
17598
  var _this = this;
17407
17599
  let {
@@ -17440,6 +17632,14 @@ class RTCEngine extends eventsExports.EventEmitter {
17440
17632
  return;
17441
17633
  }
17442
17634
  const dp = DataPacket.fromBinary(new Uint8Array(buffer));
17635
+ if (dp.sequence > 0 && dp.participantSid !== '') {
17636
+ const lastSeq = this.reliableReceivedState.get(dp.participantSid);
17637
+ if (lastSeq && dp.sequence <= lastSeq) {
17638
+ // ignore duplicate or out-of-order packets in reliable channel
17639
+ return;
17640
+ }
17641
+ this.reliableReceivedState.set(dp.participantSid, dp.sequence);
17642
+ }
17443
17643
  if (((_a = dp.value) === null || _a === void 0 ? void 0 : _a.case) === 'speaker') {
17444
17644
  // dispatch speaker updates
17445
17645
  this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.value.speakers);
@@ -17540,6 +17740,9 @@ class RTCEngine extends eventsExports.EventEmitter {
17540
17740
  this.isBufferStatusLow = kind => {
17541
17741
  const dc = this.dataChannelForKind(kind);
17542
17742
  if (dc) {
17743
+ if (kind === DataPacket_Kind.RELIABLE) {
17744
+ this.reliableMessageBuffer.alignBufferedAmount(dc.bufferedAmount);
17745
+ }
17543
17746
  return dc.bufferedAmount <= dc.bufferedAmountLowThreshold;
17544
17747
  }
17545
17748
  };
@@ -17661,6 +17864,9 @@ class RTCEngine extends eventsExports.EventEmitter {
17661
17864
  this.lossyDCSub = undefined;
17662
17865
  this.reliableDC = undefined;
17663
17866
  this.reliableDCSub = undefined;
17867
+ this.reliableMessageBuffer = new DataPacketBuffer();
17868
+ this.reliableDataSequence = 1;
17869
+ this.reliableReceivedState.clear();
17664
17870
  });
17665
17871
  }
17666
17872
  cleanupClient() {
@@ -17748,8 +17954,8 @@ class RTCEngine extends eventsExports.EventEmitter {
17748
17954
  this.pcManager.onIceCandidate = (candidate, target) => {
17749
17955
  this.client.sendIceCandidate(candidate, target);
17750
17956
  };
17751
- this.pcManager.onPublisherOffer = offer => {
17752
- this.client.sendOffer(offer);
17957
+ this.pcManager.onPublisherOffer = (offer, offerId) => {
17958
+ this.client.sendOffer(offer, offerId);
17753
17959
  };
17754
17960
  this.pcManager.onDataChannel = this.handleDataChannel;
17755
17961
  this.pcManager.onStateChange = (connectionState, publisherState, subscriberState) => __awaiter(this, void 0, void 0, function* () {
@@ -17788,14 +17994,14 @@ class RTCEngine extends eventsExports.EventEmitter {
17788
17994
  }
17789
17995
  setupSignalClientCallbacks() {
17790
17996
  // configure signaling client
17791
- this.client.onAnswer = sd => __awaiter(this, void 0, void 0, function* () {
17997
+ this.client.onAnswer = (sd, offerId) => __awaiter(this, void 0, void 0, function* () {
17792
17998
  if (!this.pcManager) {
17793
17999
  return;
17794
18000
  }
17795
18001
  this.log.debug('received server answer', Object.assign(Object.assign({}, this.logContext), {
17796
18002
  RTCSdpType: sd.type
17797
18003
  }));
17798
- yield this.pcManager.setPublisherAnswer(sd);
18004
+ yield this.pcManager.setPublisherAnswer(sd, offerId);
17799
18005
  });
17800
18006
  // add candidate on trickle
17801
18007
  this.client.onTrickle = (candidate, target) => {
@@ -17809,12 +18015,15 @@ class RTCEngine extends eventsExports.EventEmitter {
17809
18015
  this.pcManager.addIceCandidate(candidate, target);
17810
18016
  };
17811
18017
  // when server creates an offer for the client
17812
- 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;
17813
18020
  if (!this.pcManager) {
17814
18021
  return;
17815
18022
  }
17816
- const answer = yield this.pcManager.createSubscriberAnswerFromOffer(sd);
17817
- this.client.sendAnswer(answer);
18023
+ const answer = yield this.pcManager.createSubscriberAnswerFromOffer(sd, offerId);
18024
+ if (answer) {
18025
+ this.client.sendAnswer(answer, offerId);
18026
+ }
17818
18027
  });
17819
18028
  this.client.onLocalTrackPublished = res => {
17820
18029
  var _a;
@@ -18185,6 +18394,9 @@ class RTCEngine extends eventsExports.EventEmitter {
18185
18394
  if (res) {
18186
18395
  const rtcConfig = this.makeRTCConfiguration(res);
18187
18396
  this.pcManager.updateConfiguration(rtcConfig);
18397
+ if (this.latestJoinResponse) {
18398
+ this.latestJoinResponse.serverInfo = res.serverInfo;
18399
+ }
18188
18400
  } else {
18189
18401
  this.log.warn('Did not receive reconnect response', this.logContext);
18190
18402
  }
@@ -18204,6 +18416,9 @@ class RTCEngine extends eventsExports.EventEmitter {
18204
18416
  if (((_a = this.reliableDC) === null || _a === void 0 ? void 0 : _a.readyState) === 'open' && this.reliableDC.id === null) {
18205
18417
  this.createDataChannels();
18206
18418
  }
18419
+ if (res === null || res === void 0 ? void 0 : res.lastMessageSeq) {
18420
+ this.resendReliableMessagesForResume(res.lastMessageSeq);
18421
+ }
18207
18422
  // resume success
18208
18423
  this.emit(EngineEvent.Resumed);
18209
18424
  });
@@ -18276,16 +18491,42 @@ class RTCEngine extends eventsExports.EventEmitter {
18276
18491
  /* @internal */
18277
18492
  sendDataPacket(packet, kind) {
18278
18493
  return __awaiter(this, void 0, void 0, function* () {
18279
- const msg = packet.toBinary();
18280
18494
  // make sure we do have a data connection
18281
18495
  yield this.ensurePublisherConnected(kind);
18496
+ if (kind === DataPacket_Kind.RELIABLE) {
18497
+ packet.sequence = this.reliableDataSequence;
18498
+ this.reliableDataSequence += 1;
18499
+ }
18500
+ const msg = packet.toBinary();
18282
18501
  const dc = this.dataChannelForKind(kind);
18283
18502
  if (dc) {
18503
+ if (kind === DataPacket_Kind.RELIABLE) {
18504
+ this.reliableMessageBuffer.push({
18505
+ data: msg,
18506
+ sequence: packet.sequence
18507
+ });
18508
+ }
18509
+ if (this.attemptingReconnect) {
18510
+ return;
18511
+ }
18284
18512
  dc.send(msg);
18285
18513
  }
18286
18514
  this.updateAndEmitDCBufferStatus(kind);
18287
18515
  });
18288
18516
  }
18517
+ resendReliableMessagesForResume(lastMessageSeq) {
18518
+ return __awaiter(this, void 0, void 0, function* () {
18519
+ yield this.ensurePublisherConnected(DataPacket_Kind.RELIABLE);
18520
+ const dc = this.dataChannelForKind(DataPacket_Kind.RELIABLE);
18521
+ if (dc) {
18522
+ this.reliableMessageBuffer.popToSequence(lastMessageSeq);
18523
+ this.reliableMessageBuffer.getAll().forEach(msg => {
18524
+ dc.send(msg.data);
18525
+ });
18526
+ }
18527
+ this.updateAndEmitDCBufferStatus(DataPacket_Kind.RELIABLE);
18528
+ });
18529
+ }
18289
18530
  waitForBufferStatusLow(kind) {
18290
18531
  return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
18291
18532
  if (this.isBufferStatusLow(kind)) {
@@ -18477,7 +18718,13 @@ class RTCEngine extends eventsExports.EventEmitter {
18477
18718
  }),
18478
18719
  publishTracks: getTrackPublicationInfo(localTracks),
18479
18720
  dataChannels: this.dataChannelsInfo(),
18480
- trackSidsDisabled
18721
+ trackSidsDisabled,
18722
+ datachannelReceiveStates: this.reliableReceivedState.map((seq, sid) => {
18723
+ return new DataChannelReceiveState({
18724
+ publisherSid: sid,
18725
+ lastSeq: seq
18726
+ });
18727
+ })
18481
18728
  }));
18482
18729
  }
18483
18730
  /* @internal */
@@ -23588,7 +23835,7 @@ class Room extends eventsExports.EventEmitter {
23588
23835
  this.remoteParticipants.forEach((participant, identity) => {
23589
23836
  this.handleParticipantDisconnected(identity, participant);
23590
23837
  });
23591
- this.emit(RoomEvent.Moved, roomMoved.room.name, roomMoved.token);
23838
+ this.emit(RoomEvent.Moved, roomMoved.room.name);
23592
23839
  if (roomMoved.participant) {
23593
23840
  this.handleParticipantUpdates([roomMoved.participant, ...roomMoved.otherParticipants]);
23594
23841
  } else {
@@ -23818,15 +24065,15 @@ class Room extends eventsExports.EventEmitter {
23818
24065
  var _this3 = this;
23819
24066
  let exact = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
23820
24067
  return function* () {
23821
- var _a, _b, _c, _d, _e, _f, _g;
23822
- var _h;
24068
+ var _a, _b, _c, _d, _e, _f;
24069
+ var _g;
23823
24070
  let success = true;
23824
- let needsUpdateWithoutTracks = false;
24071
+ let shouldTriggerImmediateDeviceChange = false;
23825
24072
  const deviceConstraint = exact ? {
23826
24073
  exact: deviceId
23827
24074
  } : deviceId;
23828
24075
  if (kind === 'audioinput') {
23829
- needsUpdateWithoutTracks = _this3.localParticipant.audioTrackPublications.size === 0;
24076
+ shouldTriggerImmediateDeviceChange = _this3.localParticipant.audioTrackPublications.size === 0;
23830
24077
  const prevDeviceId = (_a = _this3.getActiveDevice(kind)) !== null && _a !== void 0 ? _a : _this3.options.audioCaptureDefaults.deviceId;
23831
24078
  _this3.options.audioCaptureDefaults.deviceId = deviceConstraint;
23832
24079
  const tracks = Array.from(_this3.localParticipant.audioTrackPublications.values()).filter(track => track.source === Track.Source.Microphone);
@@ -23839,8 +24086,13 @@ class Room extends eventsExports.EventEmitter {
23839
24086
  _this3.options.audioCaptureDefaults.deviceId = prevDeviceId;
23840
24087
  throw e;
23841
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;
23842
24094
  } else if (kind === 'videoinput') {
23843
- needsUpdateWithoutTracks = _this3.localParticipant.videoTrackPublications.size === 0;
24095
+ shouldTriggerImmediateDeviceChange = _this3.localParticipant.videoTrackPublications.size === 0;
23844
24096
  const prevDeviceId = (_b = _this3.getActiveDevice(kind)) !== null && _b !== void 0 ? _b : _this3.options.videoCaptureDefaults.deviceId;
23845
24097
  _this3.options.videoCaptureDefaults.deviceId = deviceConstraint;
23846
24098
  const tracks = Array.from(_this3.localParticipant.videoTrackPublications.values()).filter(track => track.source === Track.Source.Camera);
@@ -23854,6 +24106,7 @@ class Room extends eventsExports.EventEmitter {
23854
24106
  throw e;
23855
24107
  }
23856
24108
  } else if (kind === 'audiooutput') {
24109
+ shouldTriggerImmediateDeviceChange = true;
23857
24110
  if (!supportsSetSinkId() && !_this3.options.webAudioMix || _this3.options.webAudioMix && _this3.audioContext && !('setSinkId' in _this3.audioContext)) {
23858
24111
  throw new Error('cannot switch audio output, setSinkId not supported');
23859
24112
  }
@@ -23861,7 +24114,7 @@ class Room extends eventsExports.EventEmitter {
23861
24114
  // setting `default` for web audio output doesn't work, so we need to normalize the id before
23862
24115
  deviceId = (_c = yield DeviceManager.getInstance().normalizeDeviceId('audiooutput', deviceId)) !== null && _c !== void 0 ? _c : '';
23863
24116
  }
23864
- (_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 = {};
23865
24118
  const prevDeviceId = (_e = _this3.getActiveDevice(kind)) !== null && _e !== void 0 ? _e : _this3.options.audioOutput.deviceId;
23866
24119
  _this3.options.audioOutput.deviceId = deviceId;
23867
24120
  try {
@@ -23879,9 +24132,8 @@ class Room extends eventsExports.EventEmitter {
23879
24132
  throw e;
23880
24133
  }
23881
24134
  }
23882
- if (needsUpdateWithoutTracks || kind === 'audiooutput') {
23883
- // 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
23884
- _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);
23885
24137
  _this3.emit(RoomEvent.ActiveDeviceChanged, kind, deviceId);
23886
24138
  }
23887
24139
  return success;
@@ -25006,6 +25258,7 @@ class PublishVideoCheck extends Checker {
25006
25258
  };
25007
25259
  video.play();
25008
25260
  });
25261
+ stream.getTracks().forEach(t => t.stop());
25009
25262
  video.remove();
25010
25263
  });
25011
25264
  }