livekit-client 2.13.0 → 2.13.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 (35) 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 +1 -0
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +90 -49
  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/room/RTCEngine.d.ts.map +1 -1
  10. package/dist/src/room/Room.d.ts +1 -1
  11. package/dist/src/room/Room.d.ts.map +1 -1
  12. package/dist/src/room/errors.d.ts +2 -1
  13. package/dist/src/room/errors.d.ts.map +1 -1
  14. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  15. package/dist/src/room/participant/Participant.d.ts +1 -1
  16. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  17. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  18. package/dist/src/room/track/LocalVideoTrack.d.ts +1 -1
  19. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  20. package/dist/src/room/utils.d.ts +3 -0
  21. package/dist/src/room/utils.d.ts.map +1 -1
  22. package/dist/ts4.2/src/room/Room.d.ts +1 -1
  23. package/dist/ts4.2/src/room/errors.d.ts +2 -1
  24. package/dist/ts4.2/src/room/participant/Participant.d.ts +1 -1
  25. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +1 -1
  26. package/dist/ts4.2/src/room/utils.d.ts +3 -0
  27. package/package.json +11 -11
  28. package/src/room/RTCEngine.ts +5 -2
  29. package/src/room/Room.ts +9 -6
  30. package/src/room/errors.ts +1 -0
  31. package/src/room/participant/LocalParticipant.ts +36 -18
  32. package/src/room/participant/Participant.ts +1 -1
  33. package/src/room/participant/publishUtils.ts +4 -0
  34. package/src/room/track/LocalVideoTrack.ts +14 -5
  35. package/src/room/utils.ts +14 -1
@@ -7182,7 +7182,7 @@ let deprecationWarnings_ = true;
7182
7182
  */
7183
7183
  function extractVersion(uastring, expr, pos) {
7184
7184
  const match = uastring.match(expr);
7185
- return match && match.length >= pos && parseInt(match[pos], 10);
7185
+ return match && match.length >= pos && parseFloat(match[pos], 10);
7186
7186
  }
7187
7187
 
7188
7188
  // Wraps the peerconnection event eventNameToWrap in a function
@@ -7327,19 +7327,21 @@ function detectBrowser(window) {
7327
7327
  if (navigator.mozGetUserMedia) {
7328
7328
  // Firefox.
7329
7329
  result.browser = 'firefox';
7330
- result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1);
7330
+ result.version = parseInt(extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1));
7331
7331
  } else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection) {
7332
7332
  // Chrome, Chromium, Webview, Opera.
7333
7333
  // Version matches Chrome/WebRTC version.
7334
7334
  // Chrome 74 removed webkitGetUserMedia on http as well so we need the
7335
7335
  // more complicated fallback to webkitRTCPeerConnection.
7336
7336
  result.browser = 'chrome';
7337
- result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2);
7337
+ result.version = parseInt(extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2));
7338
7338
  } else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
7339
7339
  // Safari.
7340
7340
  result.browser = 'safari';
7341
- result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1);
7341
+ result.version = parseInt(extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1));
7342
7342
  result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype;
7343
+ // Only for internal usage.
7344
+ result._safariVersion = extractVersion(navigator.userAgent, /Version\/(\d+(\.?\d+))/, 1);
7343
7345
  } else {
7344
7346
  // Default fallthrough: not supported.
7345
7347
  result.browser = 'Not a supported browser.';
@@ -9940,7 +9942,7 @@ function removeExtmapAllowMixed(window, browserDetails) {
9940
9942
  if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
9941
9943
  return;
9942
9944
  }
9943
- if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
9945
+ if (browserDetails.browser === 'safari' && browserDetails._safariVersion >= 13.1) {
9944
9946
  return;
9945
9947
  }
9946
9948
  const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
@@ -10477,6 +10479,7 @@ var ConnectionErrorReason;
10477
10479
  ConnectionErrorReason[ConnectionErrorReason["InternalError"] = 2] = "InternalError";
10478
10480
  ConnectionErrorReason[ConnectionErrorReason["Cancelled"] = 3] = "Cancelled";
10479
10481
  ConnectionErrorReason[ConnectionErrorReason["LeaveRequest"] = 4] = "LeaveRequest";
10482
+ ConnectionErrorReason[ConnectionErrorReason["Timeout"] = 5] = "Timeout";
10480
10483
  })(ConnectionErrorReason || (ConnectionErrorReason = {}));
10481
10484
  class ConnectionError extends LivekitError {
10482
10485
  constructor(message, reason, status, context) {
@@ -11259,7 +11262,7 @@ function getOSVersion(ua) {
11259
11262
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11260
11263
  }
11261
11264
 
11262
- var version$1 = "2.13.0";
11265
+ var version$1 = "2.13.1";
11263
11266
 
11264
11267
  const version = version$1;
11265
11268
  const protocolVersion = 16;
@@ -11869,10 +11872,21 @@ function isSafari() {
11869
11872
  var _a;
11870
11873
  return ((_a = getBrowser()) === null || _a === void 0 ? void 0 : _a.name) === 'Safari';
11871
11874
  }
11875
+ function isSafariBased() {
11876
+ const b = getBrowser();
11877
+ return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' || (b === null || b === void 0 ? void 0 : b.os) === 'iOS';
11878
+ }
11872
11879
  function isSafari17() {
11873
11880
  const b = getBrowser();
11874
11881
  return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' && b.version.startsWith('17.');
11875
11882
  }
11883
+ function isSafariSvcApi(browser) {
11884
+ if (!browser) {
11885
+ browser = getBrowser();
11886
+ }
11887
+ // Safari 18.4 requires legacy svc api and scaleResolutionDown to be set
11888
+ return (browser === null || browser === void 0 ? void 0 : browser.name) === 'Safari' && compareVersions(browser.version, '18.3') > 0;
11889
+ }
11876
11890
  function isMobile() {
11877
11891
  var _a, _b;
11878
11892
  if (!isWeb()) return false;
@@ -16349,12 +16363,15 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
16349
16363
  // TODO: when we upstream libwebrtc, this will need additional verification
16350
16364
  isReactNative() || (browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && compareVersions(browser === null || browser === void 0 ? void 0 : browser.version, '113') < 0) {
16351
16365
  const bitratesRatio = sm.suffix == 'h' ? 2 : 3;
16366
+ // safari 18.4 uses a different svc API that requires scaleResolutionDownBy to be set.
16367
+ const requireScale = isSafariSvcApi(browser);
16352
16368
  for (let i = 0; i < sm.spatial; i += 1) {
16353
16369
  // in legacy SVC, scaleResolutionDownBy cannot be set
16354
16370
  encodings.push({
16355
16371
  rid: videoRids[2 - i],
16356
16372
  maxBitrate: videoEncoding.maxBitrate / Math.pow(bitratesRatio, i),
16357
- maxFramerate: original.encoding.maxFramerate
16373
+ maxFramerate: original.encoding.maxFramerate,
16374
+ scaleResolutionDownBy: requireScale ? Math.pow(2, i) : undefined
16358
16375
  });
16359
16376
  }
16360
16377
  // legacy SVC, scalabilityMode is set only on the first encoding
@@ -16861,7 +16878,7 @@ class LocalVideoTrack extends LocalTrack {
16861
16878
  }));
16862
16879
  }
16863
16880
  this.log.debug("setting publishing quality. max quality ".concat(maxQuality), this.logContext);
16864
- this.setPublishingLayers(qualities);
16881
+ this.setPublishingLayers(isSVCCodec(this.codec), qualities);
16865
16882
  }
16866
16883
  restartTrack(options) {
16867
16884
  return __awaiter(this, void 0, void 0, function* () {
@@ -16996,7 +17013,7 @@ class LocalVideoTrack extends LocalTrack {
16996
17013
  }));
16997
17014
  // only enable simulcast codec for preference codec setted
16998
17015
  if (!this.codec && codecs.length > 0) {
16999
- yield this.setPublishingLayers(codecs[0].qualities);
17016
+ yield this.setPublishingLayers(isSVCCodec(codecs[0].codec), codecs[0].qualities);
17000
17017
  return [];
17001
17018
  }
17002
17019
  this.subscribedCodecs = codecs;
@@ -17007,7 +17024,7 @@ class LocalVideoTrack extends LocalTrack {
17007
17024
  _a = false;
17008
17025
  const codec = _d;
17009
17026
  if (!this.codec || this.codec === codec.codec) {
17010
- yield this.setPublishingLayers(codec.qualities);
17027
+ yield this.setPublishingLayers(isSVCCodec(codec.codec), codec.qualities);
17011
17028
  } else {
17012
17029
  const simulcastCodecInfo = this.simulcastCodecs.get(codec.codec);
17013
17030
  this.log.debug("try setPublishingCodec for ".concat(codec.codec), Object.assign(Object.assign({}, this.logContext), {
@@ -17022,7 +17039,7 @@ class LocalVideoTrack extends LocalTrack {
17022
17039
  }
17023
17040
  } else if (simulcastCodecInfo.encodings) {
17024
17041
  this.log.debug("try setPublishingLayersForSender ".concat(codec.codec), this.logContext);
17025
- yield setPublishingLayersForSender(simulcastCodecInfo.sender, simulcastCodecInfo.encodings, codec.qualities, this.senderLock, this.log, this.logContext);
17042
+ yield setPublishingLayersForSender(simulcastCodecInfo.sender, simulcastCodecInfo.encodings, codec.qualities, this.senderLock, isSVCCodec(codec.codec), this.log, this.logContext);
17026
17043
  }
17027
17044
  }
17028
17045
  }
@@ -17044,7 +17061,7 @@ class LocalVideoTrack extends LocalTrack {
17044
17061
  * @internal
17045
17062
  * Sets layers that should be publishing
17046
17063
  */
17047
- setPublishingLayers(qualities) {
17064
+ setPublishingLayers(isSvc, qualities) {
17048
17065
  return __awaiter(this, void 0, void 0, function* () {
17049
17066
  this.log.debug('setting publishing layers', Object.assign(Object.assign({}, this.logContext), {
17050
17067
  qualities
@@ -17052,7 +17069,7 @@ class LocalVideoTrack extends LocalTrack {
17052
17069
  if (!this.sender || !this.encodings) {
17053
17070
  return;
17054
17071
  }
17055
- yield setPublishingLayersForSender(this.sender, this.encodings, qualities, this.senderLock, this.log, this.logContext);
17072
+ yield setPublishingLayersForSender(this.sender, this.encodings, qualities, this.senderLock, isSvc, this.log, this.logContext);
17056
17073
  });
17057
17074
  }
17058
17075
  handleAppVisibilityChanged() {
@@ -17070,7 +17087,7 @@ class LocalVideoTrack extends LocalTrack {
17070
17087
  });
17071
17088
  }
17072
17089
  }
17073
- function setPublishingLayersForSender(sender, senderEncodings, qualities, senderLock, log, logContext) {
17090
+ function setPublishingLayersForSender(sender, senderEncodings, qualities, senderLock, isSVC, log, logContext) {
17074
17091
  return __awaiter(this, void 0, void 0, function* () {
17075
17092
  const unlock = yield senderLock.lock();
17076
17093
  log.debug('setPublishingLayersForSender', Object.assign(Object.assign({}, logContext), {
@@ -17132,6 +17149,12 @@ function setPublishingLayersForSender(sender, senderEncodings, qualities, sender
17132
17149
  }
17133
17150
  }
17134
17151
  } else {
17152
+ if (isSVC) {
17153
+ const hasEnabledEncoding = qualities.some(q => q.enabled);
17154
+ if (hasEnabledEncoding) {
17155
+ qualities.forEach(q => q.enabled = true);
17156
+ }
17157
+ }
17135
17158
  // simulcast dynacast encodings
17136
17159
  encodings.forEach((encoding, idx) => {
17137
17160
  var _a;
@@ -17445,12 +17468,12 @@ class RTCEngine extends eventsExports.EventEmitter {
17445
17468
  }
17446
17469
  /** @internal */
17447
17470
  get logContext() {
17448
- var _a, _b, _c, _d, _e, _f, _g, _h;
17471
+ var _a, _b, _c, _d, _e, _f;
17449
17472
  return {
17450
17473
  room: (_b = (_a = this.latestJoinResponse) === null || _a === void 0 ? void 0 : _a.room) === null || _b === void 0 ? void 0 : _b.name,
17451
17474
  roomID: (_d = (_c = this.latestJoinResponse) === null || _c === void 0 ? void 0 : _c.room) === null || _d === void 0 ? void 0 : _d.sid,
17452
17475
  participant: (_f = (_e = this.latestJoinResponse) === null || _e === void 0 ? void 0 : _e.participant) === null || _f === void 0 ? void 0 : _f.identity,
17453
- pID: (_h = (_g = this.latestJoinResponse) === null || _g === void 0 ? void 0 : _g.participant) === null || _h === void 0 ? void 0 : _h.sid
17476
+ pID: this.participantSid
17454
17477
  };
17455
17478
  }
17456
17479
  join(url, token, opts, abortSignal) {
@@ -17551,7 +17574,7 @@ class RTCEngine extends eventsExports.EventEmitter {
17551
17574
  return new Promise((resolve, reject) => {
17552
17575
  const publicationTimeout = setTimeout(() => {
17553
17576
  delete this.pendingTrackResolvers[req.cid];
17554
- reject(new ConnectionError('publication of local track timed out, no response from server', ConnectionErrorReason.InternalError));
17577
+ reject(new ConnectionError('publication of local track timed out, no response from server', ConnectionErrorReason.Timeout));
17555
17578
  }, 10000);
17556
17579
  this.pendingTrackResolvers[req.cid] = {
17557
17580
  resolve: info => {
@@ -17727,6 +17750,9 @@ class RTCEngine extends eventsExports.EventEmitter {
17727
17750
  this.client.onRoomMoved = res => {
17728
17751
  var _a;
17729
17752
  this.participantSid = (_a = res.participant) === null || _a === void 0 ? void 0 : _a.sid;
17753
+ if (this.latestJoinResponse) {
17754
+ this.latestJoinResponse.room = res.room;
17755
+ }
17730
17756
  this.emit(EngineEvent.RoomMoved, res);
17731
17757
  };
17732
17758
  this.client.onClose = () => {
@@ -20108,7 +20134,7 @@ class LocalParticipant extends Participant {
20108
20134
  };
20109
20135
  this.handleSubscribedQualityUpdate = update => __awaiter(this, void 0, void 0, function* () {
20110
20136
  var _a, e_1, _b, _c;
20111
- var _d, _e;
20137
+ var _d;
20112
20138
  if (!((_d = this.roomOptions) === null || _d === void 0 ? void 0 : _d.dynacast)) {
20113
20139
  return;
20114
20140
  }
@@ -20119,34 +20145,30 @@ class LocalParticipant extends Participant {
20119
20145
  }));
20120
20146
  return;
20121
20147
  }
20122
- if (update.subscribedCodecs.length > 0) {
20123
- if (!pub.videoTrack) {
20124
- return;
20148
+ if (!pub.videoTrack) {
20149
+ return;
20150
+ }
20151
+ const newCodecs = yield pub.videoTrack.setPublishingCodecs(update.subscribedCodecs);
20152
+ try {
20153
+ for (var _e = true, newCodecs_1 = __asyncValues(newCodecs), newCodecs_1_1; newCodecs_1_1 = yield newCodecs_1.next(), _a = newCodecs_1_1.done, !_a; _e = true) {
20154
+ _c = newCodecs_1_1.value;
20155
+ _e = false;
20156
+ const codec = _c;
20157
+ if (isBackupCodec(codec)) {
20158
+ this.log.debug("publish ".concat(codec, " for ").concat(pub.videoTrack.sid), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)));
20159
+ yield this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options);
20160
+ }
20125
20161
  }
20126
- const newCodecs = yield pub.videoTrack.setPublishingCodecs(update.subscribedCodecs);
20162
+ } catch (e_1_1) {
20163
+ e_1 = {
20164
+ error: e_1_1
20165
+ };
20166
+ } finally {
20127
20167
  try {
20128
- for (var _f = true, newCodecs_1 = __asyncValues(newCodecs), newCodecs_1_1; newCodecs_1_1 = yield newCodecs_1.next(), _a = newCodecs_1_1.done, !_a; _f = true) {
20129
- _c = newCodecs_1_1.value;
20130
- _f = false;
20131
- const codec = _c;
20132
- if (isBackupCodec(codec)) {
20133
- this.log.debug("publish ".concat(codec, " for ").concat(pub.videoTrack.sid), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)));
20134
- yield this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options);
20135
- }
20136
- }
20137
- } catch (e_1_1) {
20138
- e_1 = {
20139
- error: e_1_1
20140
- };
20168
+ if (!_e && !_a && (_b = newCodecs_1.return)) yield _b.call(newCodecs_1);
20141
20169
  } finally {
20142
- try {
20143
- if (!_f && !_a && (_b = newCodecs_1.return)) yield _b.call(newCodecs_1);
20144
- } finally {
20145
- if (e_1) throw e_1.error;
20146
- }
20170
+ if (e_1) throw e_1.error;
20147
20171
  }
20148
- } else if (update.subscribedQualities.length > 0) {
20149
- yield (_e = pub.videoTrack) === null || _e === void 0 ? void 0 : _e.setPublishingLayers(update.subscribedQualities);
20150
20172
  }
20151
20173
  });
20152
20174
  this.handleLocalTrackUnpublished = unpublished => {
@@ -20433,7 +20455,7 @@ class LocalParticipant extends Participant {
20433
20455
  tr.stop();
20434
20456
  });
20435
20457
  if (e instanceof Error) {
20436
- this.emit(ParticipantEvent.MediaDevicesError, e);
20458
+ this.emit(ParticipantEvent.MediaDevicesError, e, sourceToKind(source));
20437
20459
  }
20438
20460
  this.pendingPublishing.delete(source);
20439
20461
  throw e;
@@ -20949,11 +20971,28 @@ class LocalParticipant extends Participant {
20949
20971
  yield this.engine.negotiate();
20950
20972
  });
20951
20973
  let ti;
20974
+ const addTrackPromise = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
20975
+ var _a;
20976
+ try {
20977
+ ti = yield this.engine.addTrack(req);
20978
+ resolve(ti);
20979
+ } catch (err) {
20980
+ if (track.sender && ((_a = this.engine.pcManager) === null || _a === void 0 ? void 0 : _a.publisher)) {
20981
+ this.engine.pcManager.publisher.removeTrack(track.sender);
20982
+ yield this.engine.negotiate().catch(negotiateErr => {
20983
+ this.log.error('failed to negotiate after removing track due to failed add track request', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
20984
+ error: negotiateErr
20985
+ }));
20986
+ });
20987
+ }
20988
+ reject(err);
20989
+ }
20990
+ }));
20952
20991
  if (this.enabledPublishVideoCodecs.length > 0) {
20953
- const rets = yield Promise.all([this.engine.addTrack(req), negotiate()]);
20992
+ const rets = yield Promise.all([addTrackPromise, negotiate()]);
20954
20993
  ti = rets[0];
20955
20994
  } else {
20956
- ti = yield this.engine.addTrack(req);
20995
+ ti = yield addTrackPromise;
20957
20996
  // server might not support the codec the client has requested, in that case, fallback
20958
20997
  // to a supported codec
20959
20998
  let primaryCodecMime;
@@ -22970,12 +23009,14 @@ class Room extends eventsExports.EventEmitter {
22970
23009
  continue;
22971
23010
  }
22972
23011
  }
22973
- if (kind === 'audioinput' && !isSafari() || kind === 'videoinput') {
23012
+ if (kind === 'audioinput' && !isSafariBased() || kind === 'videoinput') {
22974
23013
  // airpods on Safari need special handling for audioinput as the track doesn't end as soon as you take them out
22975
23014
  continue;
22976
23015
  }
22977
23016
  // switch to first available device if previously active device is not available any more
22978
- if (devicesOfKind.length > 0 && !devicesOfKind.find(deviceInfo => deviceInfo.deviceId === this.getActiveDevice(kind))) {
23017
+ if (devicesOfKind.length > 0 && !devicesOfKind.find(deviceInfo => deviceInfo.deviceId === this.getActiveDevice(kind)) && (
23018
+ // avoid switching audio output on safari without explicit user action as it leads to slowed down audio playback
23019
+ kind !== 'audiooutput' || !isSafariBased())) {
22979
23020
  yield this.switchActiveDevice(kind, devicesOfKind[0].deviceId);
22980
23021
  }
22981
23022
  }
@@ -23059,8 +23100,8 @@ class Room extends eventsExports.EventEmitter {
23059
23100
  this.onLocalConnectionQualityChanged = quality => {
23060
23101
  this.emit(RoomEvent.ConnectionQualityChanged, quality, this.localParticipant);
23061
23102
  };
23062
- this.onMediaDevicesError = e => {
23063
- this.emit(RoomEvent.MediaDevicesError, e);
23103
+ this.onMediaDevicesError = (e, kind) => {
23104
+ this.emit(RoomEvent.MediaDevicesError, e, kind);
23064
23105
  };
23065
23106
  this.onLocalParticipantPermissionsChanged = prevPermissions => {
23066
23107
  this.emit(RoomEvent.ParticipantPermissionsChanged, prevPermissions, this.localParticipant);