livekit-client 2.13.5 → 2.13.7

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.
@@ -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.7";
11329
11335
 
11330
11336
  const version = version$1;
11331
11337
  const protocolVersion = 16;
@@ -13244,10 +13250,7 @@ class SignalClient {
13244
13250
  sid,
13245
13251
  reconnectReason: reason
13246
13252
  }));
13247
- if (res instanceof ReconnectResponse) {
13248
- return res;
13249
- }
13250
- return;
13253
+ return res;
13251
13254
  });
13252
13255
  }
13253
13256
  connect(url, token, opts, abortSignal) {
@@ -13425,23 +13428,23 @@ class SignalClient {
13425
13428
  });
13426
13429
  }
13427
13430
  // initial offer after joining
13428
- sendOffer(offer) {
13431
+ sendOffer(offer, offerId) {
13429
13432
  this.log.debug('sending offer', Object.assign(Object.assign({}, this.logContext), {
13430
13433
  offerSdp: offer.sdp
13431
13434
  }));
13432
13435
  this.sendRequest({
13433
13436
  case: 'offer',
13434
- value: toProtoSessionDescription(offer)
13437
+ value: toProtoSessionDescription(offer, offerId)
13435
13438
  });
13436
13439
  }
13437
13440
  // answer a server-initiated offer
13438
- sendAnswer(answer) {
13441
+ sendAnswer(answer, offerId) {
13439
13442
  this.log.debug('sending answer', Object.assign(Object.assign({}, this.logContext), {
13440
13443
  answerSdp: answer.sdp
13441
13444
  }));
13442
13445
  return this.sendRequest({
13443
13446
  case: 'answer',
13444
- value: toProtoSessionDescription(answer)
13447
+ value: toProtoSessionDescription(answer, offerId)
13445
13448
  });
13446
13449
  }
13447
13450
  sendIceCandidate(candidate, target) {
@@ -13617,12 +13620,12 @@ class SignalClient {
13617
13620
  if (msg.case === 'answer') {
13618
13621
  const sd = fromProtoSessionDescription(msg.value);
13619
13622
  if (this.onAnswer) {
13620
- this.onAnswer(sd);
13623
+ this.onAnswer(sd, msg.value.id);
13621
13624
  }
13622
13625
  } else if (msg.case === 'offer') {
13623
13626
  const sd = fromProtoSessionDescription(msg.value);
13624
13627
  if (this.onOffer) {
13625
- this.onOffer(sd);
13628
+ this.onOffer(sd, msg.value.id);
13626
13629
  }
13627
13630
  } else if (msg.case === 'trickle') {
13628
13631
  const candidate = JSON.parse(msg.value.candidateInit);
@@ -13793,10 +13796,11 @@ function fromProtoSessionDescription(sd) {
13793
13796
  }
13794
13797
  return rsd;
13795
13798
  }
13796
- function toProtoSessionDescription(rsd) {
13799
+ function toProtoSessionDescription(rsd, id) {
13797
13800
  const sd = new SessionDescription({
13798
13801
  sdp: rsd.sdp,
13799
- type: rsd.type
13802
+ type: rsd.type,
13803
+ id
13800
13804
  });
13801
13805
  return sd;
13802
13806
  }
@@ -14741,6 +14745,7 @@ class PCTransport extends eventsExports.EventEmitter {
14741
14745
  super();
14742
14746
  this.log = livekitLogger;
14743
14747
  this.ddExtID = 0;
14748
+ this.latestOfferId = 0;
14744
14749
  this.pendingCandidates = [];
14745
14750
  this.restartingIce = false;
14746
14751
  this.renegotiate = false;
@@ -14831,9 +14836,16 @@ class PCTransport extends eventsExports.EventEmitter {
14831
14836
  this.pendingCandidates.push(candidate);
14832
14837
  });
14833
14838
  }
14834
- setRemoteDescription(sd) {
14839
+ setRemoteDescription(sd, offerId) {
14835
14840
  return __awaiter(this, void 0, void 0, function* () {
14836
14841
  var _a;
14842
+ if (sd.type === 'answer' && this.latestOfferId > 0 && offerId > 0 && offerId !== this.latestOfferId) {
14843
+ this.log.warn('ignoring answer for old offer', Object.assign(Object.assign({}, this.logContext), {
14844
+ offerId,
14845
+ latestOfferId: this.latestOfferId
14846
+ }));
14847
+ return false;
14848
+ }
14837
14849
  let mungedSDP = undefined;
14838
14850
  if (sd.type === 'offer') {
14839
14851
  let {
@@ -14907,11 +14919,15 @@ class PCTransport extends eventsExports.EventEmitter {
14907
14919
  });
14908
14920
  }
14909
14921
  }
14922
+ return true;
14910
14923
  });
14911
14924
  }
14912
14925
  createAndSendOffer(options) {
14913
14926
  return __awaiter(this, void 0, void 0, function* () {
14914
14927
  var _a;
14928
+ // 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
14929
+ const offerId = this.latestOfferId + 1;
14930
+ this.latestOfferId = offerId;
14915
14931
  if (this.onOffer === undefined) {
14916
14932
  return;
14917
14933
  }
@@ -14986,8 +15002,15 @@ class PCTransport extends eventsExports.EventEmitter {
14986
15002
  });
14987
15003
  }
14988
15004
  });
15005
+ if (this.latestOfferId > offerId) {
15006
+ this.log.warn('latestOfferId mismatch', Object.assign(Object.assign({}, this.logContext), {
15007
+ latestOfferId: this.latestOfferId,
15008
+ offerId
15009
+ }));
15010
+ return;
15011
+ }
14989
15012
  yield this.setMungedSDP(offer, libExports.write(sdpParsed));
14990
- this.onOffer(offer);
15013
+ this.onOffer(offer, this.latestOfferId);
14991
15014
  });
14992
15015
  }
14993
15016
  createAndSetAnswer() {
@@ -15382,9 +15405,9 @@ class PCTransportManager {
15382
15405
  var _a;
15383
15406
  (_a = this.onTrack) === null || _a === void 0 ? void 0 : _a.call(this, ev);
15384
15407
  };
15385
- this.publisher.onOffer = offer => {
15408
+ this.publisher.onOffer = (offer, offerId) => {
15386
15409
  var _a;
15387
- (_a = this.onPublisherOffer) === null || _a === void 0 ? void 0 : _a.call(this, offer);
15410
+ (_a = this.onPublisherOffer) === null || _a === void 0 ? void 0 : _a.call(this, offer, offerId);
15388
15411
  };
15389
15412
  this.state = PCTransportState.NEW;
15390
15413
  this.connectionLock = new _();
@@ -15407,8 +15430,8 @@ class PCTransportManager {
15407
15430
  createAndSendPublisherOffer(options) {
15408
15431
  return this.publisher.createAndSendOffer(options);
15409
15432
  }
15410
- setPublisherAnswer(sd) {
15411
- return this.publisher.setRemoteDescription(sd);
15433
+ setPublisherAnswer(sd, offerId) {
15434
+ return this.publisher.setRemoteDescription(sd, offerId);
15412
15435
  }
15413
15436
  removeTrack(sender) {
15414
15437
  return this.publisher.removeTrack(sender);
@@ -15454,7 +15477,7 @@ class PCTransportManager {
15454
15477
  }
15455
15478
  });
15456
15479
  }
15457
- createSubscriberAnswerFromOffer(sd) {
15480
+ createSubscriberAnswerFromOffer(sd, offerId) {
15458
15481
  return __awaiter(this, void 0, void 0, function* () {
15459
15482
  this.log.debug('received server offer', Object.assign(Object.assign({}, this.logContext), {
15460
15483
  RTCSdpType: sd.type,
@@ -15463,7 +15486,10 @@ class PCTransportManager {
15463
15486
  }));
15464
15487
  const unlock = yield this.remoteOfferLock.lock();
15465
15488
  try {
15466
- yield this.subscriber.setRemoteDescription(sd);
15489
+ const success = yield this.subscriber.setRemoteDescription(sd, offerId);
15490
+ if (!success) {
15491
+ return undefined;
15492
+ }
15467
15493
  // answer the offer
15468
15494
  const answer = yield this.subscriber.createAndSetAnswer();
15469
15495
  return answer;
@@ -17403,44 +17429,13 @@ function setPublishingLayersForSender(sender, senderEncodings, qualities, sender
17403
17429
  return;
17404
17430
  }
17405
17431
  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;
17432
+ /* disable closable spatial layer as it has video blur / frozen issue with current server / client
17433
+ 1. chrome 113: when switching to up layer with scalability Mode change, it will generate a
17434
+ low resolution frame and recover very quickly, but noticable
17435
+ 2. livekit sfu: additional pli request cause video frozen for a few frames, also noticable */
17436
+ const closableSpatial = false;
17408
17437
  /* @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 {
17438
+ if (closableSpatial && encodings[0].scalabilityMode) ; else {
17444
17439
  if (isSVC) {
17445
17440
  const hasEnabledEncoding = qualities.some(q => q.enabled);
17446
17441
  if (hasEnabledEncoding) {
@@ -17577,6 +17572,10 @@ class RTCEngine extends eventsExports.EventEmitter {
17577
17572
  this.rtcConfig = {};
17578
17573
  this.peerConnectionTimeout = roomConnectOptionDefaults.peerConnectionTimeout;
17579
17574
  this.fullReconnectOnNext = false;
17575
+ /**
17576
+ * @internal
17577
+ */
17578
+ this.latestRemoteOfferId = 0;
17580
17579
  this.subscriberPrimary = false;
17581
17580
  this.pcState = PCState.New;
17582
17581
  this._isClosed = true;
@@ -17953,8 +17952,8 @@ class RTCEngine extends eventsExports.EventEmitter {
17953
17952
  this.pcManager.onIceCandidate = (candidate, target) => {
17954
17953
  this.client.sendIceCandidate(candidate, target);
17955
17954
  };
17956
- this.pcManager.onPublisherOffer = offer => {
17957
- this.client.sendOffer(offer);
17955
+ this.pcManager.onPublisherOffer = (offer, offerId) => {
17956
+ this.client.sendOffer(offer, offerId);
17958
17957
  };
17959
17958
  this.pcManager.onDataChannel = this.handleDataChannel;
17960
17959
  this.pcManager.onStateChange = (connectionState, publisherState, subscriberState) => __awaiter(this, void 0, void 0, function* () {
@@ -17993,14 +17992,14 @@ class RTCEngine extends eventsExports.EventEmitter {
17993
17992
  }
17994
17993
  setupSignalClientCallbacks() {
17995
17994
  // configure signaling client
17996
- this.client.onAnswer = sd => __awaiter(this, void 0, void 0, function* () {
17995
+ this.client.onAnswer = (sd, offerId) => __awaiter(this, void 0, void 0, function* () {
17997
17996
  if (!this.pcManager) {
17998
17997
  return;
17999
17998
  }
18000
17999
  this.log.debug('received server answer', Object.assign(Object.assign({}, this.logContext), {
18001
18000
  RTCSdpType: sd.type
18002
18001
  }));
18003
- yield this.pcManager.setPublisherAnswer(sd);
18002
+ yield this.pcManager.setPublisherAnswer(sd, offerId);
18004
18003
  });
18005
18004
  // add candidate on trickle
18006
18005
  this.client.onTrickle = (candidate, target) => {
@@ -18014,12 +18013,15 @@ class RTCEngine extends eventsExports.EventEmitter {
18014
18013
  this.pcManager.addIceCandidate(candidate, target);
18015
18014
  };
18016
18015
  // when server creates an offer for the client
18017
- this.client.onOffer = sd => __awaiter(this, void 0, void 0, function* () {
18016
+ this.client.onOffer = (sd, offerId) => __awaiter(this, void 0, void 0, function* () {
18017
+ this.latestRemoteOfferId = offerId;
18018
18018
  if (!this.pcManager) {
18019
18019
  return;
18020
18020
  }
18021
- const answer = yield this.pcManager.createSubscriberAnswerFromOffer(sd);
18022
- this.client.sendAnswer(answer);
18021
+ const answer = yield this.pcManager.createSubscriberAnswerFromOffer(sd, offerId);
18022
+ if (answer) {
18023
+ this.client.sendAnswer(answer, offerId);
18024
+ }
18023
18025
  });
18024
18026
  this.client.onLocalTrackPublished = res => {
18025
18027
  var _a;
@@ -24061,15 +24063,15 @@ class Room extends eventsExports.EventEmitter {
24061
24063
  var _this3 = this;
24062
24064
  let exact = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
24063
24065
  return function* () {
24064
- var _a, _b, _c, _d, _e, _f, _g;
24065
- var _h;
24066
+ var _a, _b, _c, _d, _e, _f;
24067
+ var _g;
24066
24068
  let success = true;
24067
- let needsUpdateWithoutTracks = false;
24069
+ let shouldTriggerImmediateDeviceChange = false;
24068
24070
  const deviceConstraint = exact ? {
24069
24071
  exact: deviceId
24070
24072
  } : deviceId;
24071
24073
  if (kind === 'audioinput') {
24072
- needsUpdateWithoutTracks = _this3.localParticipant.audioTrackPublications.size === 0;
24074
+ shouldTriggerImmediateDeviceChange = _this3.localParticipant.audioTrackPublications.size === 0;
24073
24075
  const prevDeviceId = (_a = _this3.getActiveDevice(kind)) !== null && _a !== void 0 ? _a : _this3.options.audioCaptureDefaults.deviceId;
24074
24076
  _this3.options.audioCaptureDefaults.deviceId = deviceConstraint;
24075
24077
  const tracks = Array.from(_this3.localParticipant.audioTrackPublications.values()).filter(track => track.source === Track.Source.Microphone);
@@ -24082,8 +24084,13 @@ class Room extends eventsExports.EventEmitter {
24082
24084
  _this3.options.audioCaptureDefaults.deviceId = prevDeviceId;
24083
24085
  throw e;
24084
24086
  }
24087
+ const isMuted = tracks.some(t => {
24088
+ var _a, _b;
24089
+ return (_b = (_a = t.track) === null || _a === void 0 ? void 0 : _a.isMuted) !== null && _b !== void 0 ? _b : false;
24090
+ });
24091
+ if (success && isMuted) shouldTriggerImmediateDeviceChange = true;
24085
24092
  } else if (kind === 'videoinput') {
24086
- needsUpdateWithoutTracks = _this3.localParticipant.videoTrackPublications.size === 0;
24093
+ shouldTriggerImmediateDeviceChange = _this3.localParticipant.videoTrackPublications.size === 0;
24087
24094
  const prevDeviceId = (_b = _this3.getActiveDevice(kind)) !== null && _b !== void 0 ? _b : _this3.options.videoCaptureDefaults.deviceId;
24088
24095
  _this3.options.videoCaptureDefaults.deviceId = deviceConstraint;
24089
24096
  const tracks = Array.from(_this3.localParticipant.videoTrackPublications.values()).filter(track => track.source === Track.Source.Camera);
@@ -24097,6 +24104,7 @@ class Room extends eventsExports.EventEmitter {
24097
24104
  throw e;
24098
24105
  }
24099
24106
  } else if (kind === 'audiooutput') {
24107
+ shouldTriggerImmediateDeviceChange = true;
24100
24108
  if (!supportsSetSinkId() && !_this3.options.webAudioMix || _this3.options.webAudioMix && _this3.audioContext && !('setSinkId' in _this3.audioContext)) {
24101
24109
  throw new Error('cannot switch audio output, setSinkId not supported');
24102
24110
  }
@@ -24104,7 +24112,7 @@ class Room extends eventsExports.EventEmitter {
24104
24112
  // setting `default` for web audio output doesn't work, so we need to normalize the id before
24105
24113
  deviceId = (_c = yield DeviceManager.getInstance().normalizeDeviceId('audiooutput', deviceId)) !== null && _c !== void 0 ? _c : '';
24106
24114
  }
24107
- (_d = (_h = _this3.options).audioOutput) !== null && _d !== void 0 ? _d : _h.audioOutput = {};
24115
+ (_d = (_g = _this3.options).audioOutput) !== null && _d !== void 0 ? _d : _g.audioOutput = {};
24108
24116
  const prevDeviceId = (_e = _this3.getActiveDevice(kind)) !== null && _e !== void 0 ? _e : _this3.options.audioOutput.deviceId;
24109
24117
  _this3.options.audioOutput.deviceId = deviceId;
24110
24118
  try {
@@ -24122,9 +24130,8 @@ class Room extends eventsExports.EventEmitter {
24122
24130
  throw e;
24123
24131
  }
24124
24132
  }
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);
24133
+ if (shouldTriggerImmediateDeviceChange) {
24134
+ _this3.localParticipant.activeDeviceMap.set(kind, deviceId);
24128
24135
  _this3.emit(RoomEvent.ActiveDeviceChanged, kind, deviceId);
24129
24136
  }
24130
24137
  return success;
@@ -25249,6 +25256,7 @@ class PublishVideoCheck extends Checker {
25249
25256
  };
25250
25257
  video.play();
25251
25258
  });
25259
+ stream.getTracks().forEach(t => t.stop());
25252
25260
  video.remove();
25253
25261
  });
25254
25262
  }