livekit-client 2.13.7 → 2.14.0

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 (62) 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 +13 -7
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +188 -90
  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/e2ee/E2eeManager.d.ts.map +1 -1
  10. package/dist/src/e2ee/types.d.ts +7 -0
  11. package/dist/src/e2ee/types.d.ts.map +1 -1
  12. package/dist/src/e2ee/worker/FrameCryptor.d.ts +2 -1
  13. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  14. package/dist/src/room/PCTransport.d.ts +1 -0
  15. package/dist/src/room/PCTransport.d.ts.map +1 -1
  16. package/dist/src/room/events.d.ts +18 -0
  17. package/dist/src/room/events.d.ts.map +1 -1
  18. package/dist/src/room/participant/LocalParticipant.d.ts +1 -0
  19. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  20. package/dist/src/room/participant/Participant.d.ts +3 -0
  21. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  22. package/dist/src/room/track/LocalTrackPublication.d.ts +1 -0
  23. package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
  24. package/dist/src/room/track/LocalVideoTrack.d.ts +7 -0
  25. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  26. package/dist/src/room/track/Track.d.ts +1 -0
  27. package/dist/src/room/track/Track.d.ts.map +1 -1
  28. package/dist/src/room/track/TrackPublication.d.ts +1 -0
  29. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  30. package/dist/src/room/track/options.d.ts +4 -0
  31. package/dist/src/room/track/options.d.ts.map +1 -1
  32. package/dist/src/room/utils.d.ts +1 -1
  33. package/dist/src/room/utils.d.ts.map +1 -1
  34. package/dist/ts4.2/src/e2ee/types.d.ts +7 -0
  35. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +2 -1
  36. package/dist/ts4.2/src/room/PCTransport.d.ts +1 -0
  37. package/dist/ts4.2/src/room/events.d.ts +18 -0
  38. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +1 -0
  39. package/dist/ts4.2/src/room/participant/Participant.d.ts +3 -0
  40. package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -0
  41. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +7 -0
  42. package/dist/ts4.2/src/room/track/Track.d.ts +1 -0
  43. package/dist/ts4.2/src/room/track/TrackPublication.d.ts +1 -0
  44. package/dist/ts4.2/src/room/track/options.d.ts +4 -0
  45. package/dist/ts4.2/src/room/utils.d.ts +1 -1
  46. package/package.json +1 -1
  47. package/src/e2ee/E2eeManager.ts +8 -3
  48. package/src/e2ee/types.ts +8 -0
  49. package/src/e2ee/worker/FrameCryptor.ts +15 -0
  50. package/src/e2ee/worker/e2ee.worker.ts +8 -5
  51. package/src/room/PCTransport.ts +88 -77
  52. package/src/room/events.ts +21 -0
  53. package/src/room/participant/LocalParticipant.ts +14 -2
  54. package/src/room/participant/Participant.ts +3 -0
  55. package/src/room/participant/publishUtils.ts +2 -2
  56. package/src/room/track/LocalTrackPublication.ts +9 -1
  57. package/src/room/track/LocalVideoTrack.ts +68 -1
  58. package/src/room/track/Track.ts +1 -0
  59. package/src/room/track/TrackPublication.ts +1 -0
  60. package/src/room/track/create.ts +2 -2
  61. package/src/room/track/options.ts +5 -0
  62. package/src/room/utils.ts +19 -3
@@ -11025,6 +11025,19 @@ var ParticipantEvent;
11025
11025
  * args: ([[LocalTrackPublication]])
11026
11026
  */
11027
11027
  ParticipantEvent["LocalTrackUnpublished"] = "localTrackUnpublished";
11028
+ /**
11029
+ * A local track has been constrained by cpu.
11030
+ * This event is useful to know when to reduce the capture resolution of the track.
11031
+ *
11032
+ * This event is emitted on the local participant.
11033
+ *
11034
+ * args: ([[LocalVideoTrack]], [[LocalTrackPublication]])
11035
+ */
11036
+ ParticipantEvent["LocalTrackCpuConstrained"] = "localTrackCpuConstrained";
11037
+ /**
11038
+ * @internal
11039
+ */
11040
+ ParticipantEvent["LocalSenderCreated"] = "localSenderCreated";
11028
11041
  /**
11029
11042
  * Participant metadata is a simple way for app-specific state to be pushed to
11030
11043
  * all users.
@@ -11101,6 +11114,10 @@ var ParticipantEvent;
11101
11114
  *
11102
11115
  */
11103
11116
  ParticipantEvent["TrackSubscriptionStatusChanged"] = "trackSubscriptionStatusChanged";
11117
+ /**
11118
+ * a local track has been constrained by cpu
11119
+ */
11120
+ ParticipantEvent["TrackCpuConstrained"] = "trackCpuConstrained";
11104
11121
  // fired only on LocalParticipant
11105
11122
  /** @internal */
11106
11123
  ParticipantEvent["MediaDevicesError"] = "mediaDevicesError";
@@ -11178,6 +11195,7 @@ var TrackEvent;
11178
11195
  TrackEvent["Ended"] = "ended";
11179
11196
  TrackEvent["Subscribed"] = "subscribed";
11180
11197
  TrackEvent["Unsubscribed"] = "unsubscribed";
11198
+ TrackEvent["CpuConstrained"] = "cpuConstrained";
11181
11199
  /** @internal */
11182
11200
  TrackEvent["UpdateSettings"] = "updateSettings";
11183
11201
  /** @internal */
@@ -11331,7 +11349,7 @@ function getOSVersion(ua) {
11331
11349
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11332
11350
  }
11333
11351
 
11334
- var version$1 = "2.13.7";
11352
+ var version$1 = "2.14.0";
11335
11353
 
11336
11354
  const version = version$1;
11337
11355
  const protocolVersion = 16;
@@ -11902,6 +11920,10 @@ function supportsVP9() {
11902
11920
  // Safari 16 and below does not support VP9
11903
11921
  return false;
11904
11922
  }
11923
+ if ((browser === null || browser === void 0 ? void 0 : browser.os) === 'iOS' && (browser === null || browser === void 0 ? void 0 : browser.osVersion) && compareVersions(browser.osVersion, '16') < 0) {
11924
+ // Safari 16 and below on iOS does not support VP9 we need the iOS check to account for other browsers running webkit under the hood
11925
+ return false;
11926
+ }
11905
11927
  }
11906
11928
  const capabilities = RTCRtpSender.getCapabilities('video');
11907
11929
  let hasVP9 = false;
@@ -11945,16 +11967,16 @@ function isSafariBased() {
11945
11967
  const b = getBrowser();
11946
11968
  return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' || (b === null || b === void 0 ? void 0 : b.os) === 'iOS';
11947
11969
  }
11948
- function isSafari17() {
11970
+ function isSafari17Based() {
11949
11971
  const b = getBrowser();
11950
- return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' && b.version.startsWith('17.');
11972
+ return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' && b.version.startsWith('17.') || (b === null || b === void 0 ? void 0 : b.os) === 'iOS' && !!(b === null || b === void 0 ? void 0 : b.osVersion) && compareVersions(b.osVersion, '17') >= 0;
11951
11973
  }
11952
11974
  function isSafariSvcApi(browser) {
11953
11975
  if (!browser) {
11954
11976
  browser = getBrowser();
11955
11977
  }
11956
11978
  // Safari 18.4 requires legacy svc api and scaleResolutionDown to be set
11957
- return (browser === null || browser === void 0 ? void 0 : browser.name) === 'Safari' && compareVersions(browser.version, '18.3') > 0;
11979
+ return (browser === null || browser === void 0 ? void 0 : browser.name) === 'Safari' && compareVersions(browser.version, '18.3') > 0 || (browser === null || browser === void 0 ? void 0 : browser.os) === 'iOS' && !!(browser === null || browser === void 0 ? void 0 : browser.osVersion) && compareVersions(browser.osVersion, '18.3') > 0;
11958
11980
  }
11959
11981
  function isMobile() {
11960
11982
  var _a, _b;
@@ -12799,8 +12821,8 @@ class E2EEManager extends eventsExports.EventEmitter {
12799
12821
  });
12800
12822
  this.setParticipantCryptorEnabled(this.room.localParticipant.isE2EEEnabled, this.room.localParticipant.identity);
12801
12823
  });
12802
- room.localParticipant.on(ParticipantEvent.LocalTrackPublished, publication => __awaiter(this, void 0, void 0, function* () {
12803
- this.setupE2EESender(publication.track, publication.track.sender);
12824
+ room.localParticipant.on(ParticipantEvent.LocalSenderCreated, (sender, track) => __awaiter(this, void 0, void 0, function* () {
12825
+ this.setupE2EESender(track, sender);
12804
12826
  }));
12805
12827
  keyProvider.on(KeyProviderEvent.SetKey, keyInfo => this.postKey(keyInfo)).on(KeyProviderEvent.RatchetRequest, (participantId, keyIndex) => this.postRatchetRequest(participantId, keyIndex));
12806
12828
  }
@@ -12951,7 +12973,8 @@ class E2EEManager extends eventsExports.EventEmitter {
12951
12973
  writableStream: writable,
12952
12974
  trackId: trackId,
12953
12975
  codec,
12954
- participantIdentity: participantIdentity
12976
+ participantIdentity: participantIdentity,
12977
+ isReuse: E2EE_FLAG in receiver
12955
12978
  }
12956
12979
  };
12957
12980
  this.worker.postMessage(msg, [readable, writable]);
@@ -12994,7 +13017,8 @@ class E2EEManager extends eventsExports.EventEmitter {
12994
13017
  writableStream: senderStreams.writable,
12995
13018
  codec,
12996
13019
  trackId,
12997
- participantIdentity: this.room.localParticipant.identity
13020
+ participantIdentity: this.room.localParticipant.identity,
13021
+ isReuse: false
12998
13022
  }
12999
13023
  };
13000
13024
  this.worker.postMessage(msg, [senderStreams.readable, senderStreams.writable]);
@@ -14787,6 +14811,7 @@ class PCTransport extends eventsExports.EventEmitter {
14787
14811
  this.loggerOptions = loggerOptions;
14788
14812
  this.config = config;
14789
14813
  this._pc = this.createPC();
14814
+ this.offerLock = new _();
14790
14815
  }
14791
14816
  createPC() {
14792
14817
  const pc = new RTCPeerConnection(this.config);
@@ -14925,92 +14950,97 @@ class PCTransport extends eventsExports.EventEmitter {
14925
14950
  createAndSendOffer(options) {
14926
14951
  return __awaiter(this, void 0, void 0, function* () {
14927
14952
  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;
14931
- if (this.onOffer === undefined) {
14932
- return;
14933
- }
14934
- if (options === null || options === void 0 ? void 0 : options.iceRestart) {
14935
- this.log.debug('restarting ICE', this.logContext);
14936
- this.restartingIce = true;
14937
- }
14938
- if (this._pc && this._pc.signalingState === 'have-local-offer') {
14939
- // we're waiting for the peer to accept our offer, so we'll just wait
14940
- // the only exception to this is when ICE restart is needed
14941
- const currentSD = this._pc.remoteDescription;
14942
- if ((options === null || options === void 0 ? void 0 : options.iceRestart) && currentSD) {
14943
- // TODO: handle when ICE restart is needed but we don't have a remote description
14944
- // the best thing to do is to recreate the peerconnection
14945
- yield this._pc.setRemoteDescription(currentSD);
14946
- } else {
14947
- this.renegotiate = true;
14953
+ const unlock = yield this.offerLock.lock();
14954
+ try {
14955
+ // 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
14956
+ const offerId = this.latestOfferId + 1;
14957
+ this.latestOfferId = offerId;
14958
+ if (this.onOffer === undefined) {
14948
14959
  return;
14949
14960
  }
14950
- } else if (!this._pc || this._pc.signalingState === 'closed') {
14951
- this.log.warn('could not createOffer with closed peer connection', this.logContext);
14952
- return;
14953
- }
14954
- // actually negotiate
14955
- this.log.debug('starting to negotiate', this.logContext);
14956
- const offer = yield this.pc.createOffer(options);
14957
- this.log.debug('original offer', Object.assign({
14958
- sdp: offer.sdp
14959
- }, this.logContext));
14960
- const sdpParsed = libExports.parse((_a = offer.sdp) !== null && _a !== void 0 ? _a : '');
14961
- sdpParsed.media.forEach(media => {
14962
- ensureIPAddrMatchVersion(media);
14963
- if (media.type === 'audio') {
14964
- ensureAudioNackAndStereo(media, [], []);
14965
- } else if (media.type === 'video') {
14966
- this.trackBitrates.some(trackbr => {
14967
- if (!media.msid || !trackbr.cid || !media.msid.includes(trackbr.cid)) {
14968
- return false;
14969
- }
14970
- let codecPayload = 0;
14971
- media.rtp.some(rtp => {
14972
- if (rtp.codec.toUpperCase() === trackbr.codec.toUpperCase()) {
14973
- codecPayload = rtp.payload;
14961
+ if (options === null || options === void 0 ? void 0 : options.iceRestart) {
14962
+ this.log.debug('restarting ICE', this.logContext);
14963
+ this.restartingIce = true;
14964
+ }
14965
+ if (this._pc && this._pc.signalingState === 'have-local-offer') {
14966
+ // we're waiting for the peer to accept our offer, so we'll just wait
14967
+ // the only exception to this is when ICE restart is needed
14968
+ const currentSD = this._pc.remoteDescription;
14969
+ if ((options === null || options === void 0 ? void 0 : options.iceRestart) && currentSD) {
14970
+ // TODO: handle when ICE restart is needed but we don't have a remote description
14971
+ // the best thing to do is to recreate the peerconnection
14972
+ yield this._pc.setRemoteDescription(currentSD);
14973
+ } else {
14974
+ this.renegotiate = true;
14975
+ return;
14976
+ }
14977
+ } else if (!this._pc || this._pc.signalingState === 'closed') {
14978
+ this.log.warn('could not createOffer with closed peer connection', this.logContext);
14979
+ return;
14980
+ }
14981
+ // actually negotiate
14982
+ this.log.debug('starting to negotiate', this.logContext);
14983
+ const offer = yield this.pc.createOffer(options);
14984
+ this.log.debug('original offer', Object.assign({
14985
+ sdp: offer.sdp
14986
+ }, this.logContext));
14987
+ const sdpParsed = libExports.parse((_a = offer.sdp) !== null && _a !== void 0 ? _a : '');
14988
+ sdpParsed.media.forEach(media => {
14989
+ ensureIPAddrMatchVersion(media);
14990
+ if (media.type === 'audio') {
14991
+ ensureAudioNackAndStereo(media, [], []);
14992
+ } else if (media.type === 'video') {
14993
+ this.trackBitrates.some(trackbr => {
14994
+ if (!media.msid || !trackbr.cid || !media.msid.includes(trackbr.cid)) {
14995
+ return false;
14996
+ }
14997
+ let codecPayload = 0;
14998
+ media.rtp.some(rtp => {
14999
+ if (rtp.codec.toUpperCase() === trackbr.codec.toUpperCase()) {
15000
+ codecPayload = rtp.payload;
15001
+ return true;
15002
+ }
15003
+ return false;
15004
+ });
15005
+ if (codecPayload === 0) {
14974
15006
  return true;
14975
15007
  }
14976
- return false;
14977
- });
14978
- if (codecPayload === 0) {
14979
- return true;
14980
- }
14981
- if (isSVCCodec(trackbr.codec)) {
14982
- this.ensureVideoDDExtensionForSVC(media, sdpParsed);
14983
- }
14984
- // TODO: av1 slow starting issue already fixed in chrome 124, clean this after some versions
14985
- // mung sdp for av1 bitrate setting that can't apply by sendEncoding
14986
- if (trackbr.codec !== 'av1') {
14987
- return true;
14988
- }
14989
- const startBitrate = Math.round(trackbr.maxbr * startBitrateForSVC);
14990
- for (const fmtp of media.fmtp) {
14991
- if (fmtp.payload === codecPayload) {
14992
- // if another track's fmtp already is set, we cannot override the bitrate
14993
- // this has the unfortunate consequence of being forced to use the
14994
- // initial track's bitrate for all tracks
14995
- if (!fmtp.config.includes('x-google-start-bitrate')) {
14996
- fmtp.config += ";x-google-start-bitrate=".concat(startBitrate);
15008
+ if (isSVCCodec(trackbr.codec)) {
15009
+ this.ensureVideoDDExtensionForSVC(media, sdpParsed);
15010
+ }
15011
+ // TODO: av1 slow starting issue already fixed in chrome 124, clean this after some versions
15012
+ // mung sdp for av1 bitrate setting that can't apply by sendEncoding
15013
+ if (trackbr.codec !== 'av1') {
15014
+ return true;
15015
+ }
15016
+ const startBitrate = Math.round(trackbr.maxbr * startBitrateForSVC);
15017
+ for (const fmtp of media.fmtp) {
15018
+ if (fmtp.payload === codecPayload) {
15019
+ // if another track's fmtp already is set, we cannot override the bitrate
15020
+ // this has the unfortunate consequence of being forced to use the
15021
+ // initial track's bitrate for all tracks
15022
+ if (!fmtp.config.includes('x-google-start-bitrate')) {
15023
+ fmtp.config += ";x-google-start-bitrate=".concat(startBitrate);
15024
+ }
15025
+ break;
14997
15026
  }
14998
- break;
14999
15027
  }
15000
- }
15001
- return true;
15002
- });
15028
+ return true;
15029
+ });
15030
+ }
15031
+ });
15032
+ if (this.latestOfferId > offerId) {
15033
+ this.log.warn('latestOfferId mismatch', Object.assign(Object.assign({}, this.logContext), {
15034
+ latestOfferId: this.latestOfferId,
15035
+ offerId
15036
+ }));
15037
+ return;
15003
15038
  }
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;
15039
+ yield this.setMungedSDP(offer, libExports.write(sdpParsed));
15040
+ this.onOffer(offer, this.latestOfferId);
15041
+ } finally {
15042
+ unlock();
15011
15043
  }
15012
- yield this.setMungedSDP(offer, libExports.write(sdpParsed));
15013
- this.onOffer(offer, this.latestOfferId);
15014
15044
  });
15015
15045
  }
15016
15046
  createAndSetAnswer() {
@@ -16675,7 +16705,7 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
16675
16705
  // before M113.
16676
16706
  // Announced here: https://groups.google.com/g/discuss-webrtc/c/-QQ3pxrl-fw?pli=1
16677
16707
  const browser = getBrowser();
16678
- if (isSafari() ||
16708
+ if (isSafariBased() ||
16679
16709
  // Even tho RN runs M114, it does not produce SVC layers when a single encoding
16680
16710
  // is provided. So we'll use the legacy SVC specification for now.
16681
16711
  // TODO: when we upstream libwebrtc, this will need additional verification
@@ -16962,6 +16992,8 @@ class LocalVideoTrack extends LocalTrack {
16962
16992
  /* @internal */
16963
16993
  this.simulcastCodecs = new Map();
16964
16994
  this.degradationPreference = 'balanced';
16995
+ this.isCpuConstrained = false;
16996
+ this.optimizeForPerformance = false;
16965
16997
  this.monitorSender = () => __awaiter(this, void 0, void 0, function* () {
16966
16998
  if (!this.sender) {
16967
16999
  this._currentBitrate = 0;
@@ -16971,12 +17003,19 @@ class LocalVideoTrack extends LocalTrack {
16971
17003
  try {
16972
17004
  stats = yield this.getSenderStats();
16973
17005
  } catch (e) {
16974
- this.log.error('could not get audio sender stats', Object.assign(Object.assign({}, this.logContext), {
17006
+ this.log.error('could not get video sender stats', Object.assign(Object.assign({}, this.logContext), {
16975
17007
  error: e
16976
17008
  }));
16977
17009
  return;
16978
17010
  }
16979
17011
  const statsMap = new Map(stats.map(s => [s.rid, s]));
17012
+ const isCpuConstrained = stats.some(s => s.qualityLimitationReason === 'cpu');
17013
+ if (isCpuConstrained !== this.isCpuConstrained) {
17014
+ this.isCpuConstrained = isCpuConstrained;
17015
+ if (this.isCpuConstrained) {
17016
+ this.emit(TrackEvent.CpuConstrained);
17017
+ }
17018
+ }
16980
17019
  if (this.prevStats) {
16981
17020
  let totalBitrate = 0;
16982
17021
  statsMap.forEach((s, key) => {
@@ -17212,6 +17251,8 @@ class LocalVideoTrack extends LocalTrack {
17212
17251
  }
17213
17252
  }
17214
17253
  yield this.restart(constraints);
17254
+ // reset cpu constrained state after track is restarted
17255
+ this.isCpuConstrained = false;
17215
17256
  try {
17216
17257
  for (var _e = true, _f = __asyncValues(this.simulcastCodecs.values()), _g; _g = yield _f.next(), _a = _g.done, !_a; _e = true) {
17217
17258
  _c = _g.value;
@@ -17381,6 +17422,12 @@ class LocalVideoTrack extends LocalTrack {
17381
17422
  */
17382
17423
  setPublishingLayers(isSvc, qualities) {
17383
17424
  return __awaiter(this, void 0, void 0, function* () {
17425
+ if (this.optimizeForPerformance) {
17426
+ this.log.info('skipping setPublishingLayers due to optimized publishing performance', Object.assign(Object.assign({}, this.logContext), {
17427
+ qualities
17428
+ }));
17429
+ return;
17430
+ }
17384
17431
  this.log.debug('setting publishing layers', Object.assign(Object.assign({}, this.logContext), {
17385
17432
  qualities
17386
17433
  }));
@@ -17390,6 +17437,44 @@ class LocalVideoTrack extends LocalTrack {
17390
17437
  yield setPublishingLayersForSender(this.sender, this.encodings, qualities, this.senderLock, isSvc, this.log, this.logContext);
17391
17438
  });
17392
17439
  }
17440
+ /**
17441
+ * Designed for lower powered devices, reduces video publishing quality and disables simulcast.
17442
+ * @experimental
17443
+ */
17444
+ prioritizePerformance() {
17445
+ return __awaiter(this, void 0, void 0, function* () {
17446
+ if (!this.sender) {
17447
+ throw new Error('sender not found');
17448
+ }
17449
+ const unlock = yield this.senderLock.lock();
17450
+ try {
17451
+ this.optimizeForPerformance = true;
17452
+ const params = this.sender.getParameters();
17453
+ params.encodings = params.encodings.map((e, idx) => {
17454
+ var _a;
17455
+ return Object.assign(Object.assign({}, e), {
17456
+ active: idx === 0,
17457
+ scaleResolutionDownBy: Math.max(1, Math.ceil(((_a = this.mediaStreamTrack.getSettings().height) !== null && _a !== void 0 ? _a : 360) / 360)),
17458
+ scalabilityMode: idx === 0 && isSVCCodec(this.codec) ? 'L1T3' : undefined,
17459
+ maxFramerate: idx === 0 ? 15 : 0,
17460
+ maxBitrate: idx === 0 ? e.maxBitrate : 0
17461
+ });
17462
+ });
17463
+ this.log.debug('setting performance optimised encodings', Object.assign(Object.assign({}, this.logContext), {
17464
+ encodings: params.encodings
17465
+ }));
17466
+ this.encodings = params.encodings;
17467
+ yield this.sender.setParameters(params);
17468
+ } catch (e) {
17469
+ this.log.error('failed to set performance optimised encodings', Object.assign(Object.assign({}, this.logContext), {
17470
+ error: e
17471
+ }));
17472
+ this.optimizeForPerformance = false;
17473
+ } finally {
17474
+ unlock();
17475
+ }
17476
+ });
17477
+ }
17393
17478
  handleAppVisibilityChanged() {
17394
17479
  const _super = Object.create(null, {
17395
17480
  handleAppVisibilityChanged: {
@@ -19816,16 +19901,23 @@ class LocalTrackPublication extends TrackPublication {
19816
19901
  this.handleTrackEnded = () => {
19817
19902
  this.emit(TrackEvent.Ended);
19818
19903
  };
19904
+ this.handleCpuConstrained = () => {
19905
+ if (this.track && isVideoTrack(this.track)) {
19906
+ this.emit(TrackEvent.CpuConstrained, this.track);
19907
+ }
19908
+ };
19819
19909
  this.updateInfo(ti);
19820
19910
  this.setTrack(track);
19821
19911
  }
19822
19912
  setTrack(track) {
19823
19913
  if (this.track) {
19824
19914
  this.track.off(TrackEvent.Ended, this.handleTrackEnded);
19915
+ this.track.off(TrackEvent.CpuConstrained, this.handleCpuConstrained);
19825
19916
  }
19826
19917
  super.setTrack(track);
19827
19918
  if (track) {
19828
19919
  track.on(TrackEvent.Ended, this.handleTrackEnded);
19920
+ track.on(TrackEvent.CpuConstrained, this.handleCpuConstrained);
19829
19921
  }
19830
19922
  }
19831
19923
  get isMuted() {
@@ -20063,7 +20155,7 @@ function createLocalScreenTracks(options) {
20063
20155
  if (options === undefined) {
20064
20156
  options = {};
20065
20157
  }
20066
- if (options.resolution === undefined && !isSafari17()) {
20158
+ if (options.resolution === undefined && !isSafari17Based()) {
20067
20159
  options.resolution = ScreenSharePresets.h1080fps30.resolution;
20068
20160
  }
20069
20161
  if (navigator.mediaDevices.getDisplayMedia === undefined) {
@@ -20496,6 +20588,10 @@ class LocalParticipant extends Participant {
20496
20588
  }
20497
20589
  this.engine.client.sendUpdateLocalAudioTrack(pub.trackSid, pub.getTrackFeatures());
20498
20590
  };
20591
+ this.onTrackCpuConstrained = (track, publication) => {
20592
+ this.log.debug('track cpu constrained', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(publication)));
20593
+ this.emit(ParticipantEvent.LocalTrackCpuConstrained, track, publication);
20594
+ };
20499
20595
  this.handleSubscribedQualityUpdate = update => __awaiter(this, void 0, void 0, function* () {
20500
20596
  var _a, e_1, _b, _c;
20501
20597
  var _d;
@@ -20955,7 +21051,7 @@ class LocalParticipant extends Participant {
20955
21051
  if (navigator.mediaDevices.getDisplayMedia === undefined) {
20956
21052
  throw new DeviceUnsupportedError('getDisplayMedia not supported');
20957
21053
  }
20958
- if (options.resolution === undefined && !isSafari17()) {
21054
+ if (options.resolution === undefined && !isSafari17Based()) {
20959
21055
  // we need to constrain the dimensions, otherwise it could lead to low bitrate
20960
21056
  // due to encoding a huge video. Encoding such large surfaces is really expensive
20961
21057
  // unfortunately Safari 17 has a but and cannot be constrained by default
@@ -21322,6 +21418,7 @@ class LocalParticipant extends Participant {
21322
21418
  throw new UnexpectedConnectionState('pcManager is not ready');
21323
21419
  }
21324
21420
  track.sender = yield this.engine.createSender(track, opts, encodings);
21421
+ this.emit(ParticipantEvent.LocalSenderCreated, track.sender, track);
21325
21422
  if (isLocalVideoTrack(track)) {
21326
21423
  (_a = opts.degradationPreference) !== null && _a !== void 0 ? _a : opts.degradationPreference = getDefaultDegradationPreference(track);
21327
21424
  track.setDegradationPreference(opts.degradationPreference);
@@ -21407,6 +21504,7 @@ class LocalParticipant extends Participant {
21407
21504
  loggerName: this.roomOptions.loggerName,
21408
21505
  loggerContextCb: () => this.logContext
21409
21506
  });
21507
+ publication.on(TrackEvent.CpuConstrained, constrainedTrack => this.onTrackCpuConstrained(constrainedTrack, publication));
21410
21508
  // save options for when it needs to be republished again
21411
21509
  publication.options = opts;
21412
21510
  track.sid = ti.sid;