livekit-client 2.13.8 → 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 (49) hide show
  1. package/dist/livekit-client.esm.mjs +184 -88
  2. package/dist/livekit-client.esm.mjs.map +1 -1
  3. package/dist/livekit-client.umd.js +1 -1
  4. package/dist/livekit-client.umd.js.map +1 -1
  5. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  6. package/dist/src/room/PCTransport.d.ts +1 -0
  7. package/dist/src/room/PCTransport.d.ts.map +1 -1
  8. package/dist/src/room/events.d.ts +18 -0
  9. package/dist/src/room/events.d.ts.map +1 -1
  10. package/dist/src/room/participant/LocalParticipant.d.ts +1 -0
  11. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  12. package/dist/src/room/participant/Participant.d.ts +3 -0
  13. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  14. package/dist/src/room/track/LocalTrackPublication.d.ts +1 -0
  15. package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
  16. package/dist/src/room/track/LocalVideoTrack.d.ts +7 -0
  17. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  18. package/dist/src/room/track/Track.d.ts +1 -0
  19. package/dist/src/room/track/Track.d.ts.map +1 -1
  20. package/dist/src/room/track/TrackPublication.d.ts +1 -0
  21. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  22. package/dist/src/room/track/options.d.ts +4 -0
  23. package/dist/src/room/track/options.d.ts.map +1 -1
  24. package/dist/src/room/utils.d.ts +1 -1
  25. package/dist/src/room/utils.d.ts.map +1 -1
  26. package/dist/ts4.2/src/room/PCTransport.d.ts +1 -0
  27. package/dist/ts4.2/src/room/events.d.ts +18 -0
  28. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +1 -0
  29. package/dist/ts4.2/src/room/participant/Participant.d.ts +3 -0
  30. package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -0
  31. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +7 -0
  32. package/dist/ts4.2/src/room/track/Track.d.ts +1 -0
  33. package/dist/ts4.2/src/room/track/TrackPublication.d.ts +1 -0
  34. package/dist/ts4.2/src/room/track/options.d.ts +4 -0
  35. package/dist/ts4.2/src/room/utils.d.ts +1 -1
  36. package/package.json +1 -1
  37. package/src/e2ee/E2eeManager.ts +3 -2
  38. package/src/room/PCTransport.ts +88 -77
  39. package/src/room/events.ts +21 -0
  40. package/src/room/participant/LocalParticipant.ts +14 -2
  41. package/src/room/participant/Participant.ts +3 -0
  42. package/src/room/participant/publishUtils.ts +2 -2
  43. package/src/room/track/LocalTrackPublication.ts +9 -1
  44. package/src/room/track/LocalVideoTrack.ts +68 -1
  45. package/src/room/track/Track.ts +1 -0
  46. package/src/room/track/TrackPublication.ts +1 -0
  47. package/src/room/track/create.ts +2 -2
  48. package/src/room/track/options.ts +5 -0
  49. 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.8";
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
  }
@@ -14789,6 +14811,7 @@ class PCTransport extends eventsExports.EventEmitter {
14789
14811
  this.loggerOptions = loggerOptions;
14790
14812
  this.config = config;
14791
14813
  this._pc = this.createPC();
14814
+ this.offerLock = new _();
14792
14815
  }
14793
14816
  createPC() {
14794
14817
  const pc = new RTCPeerConnection(this.config);
@@ -14927,92 +14950,97 @@ class PCTransport extends eventsExports.EventEmitter {
14927
14950
  createAndSendOffer(options) {
14928
14951
  return __awaiter(this, void 0, void 0, function* () {
14929
14952
  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;
14933
- if (this.onOffer === undefined) {
14934
- return;
14935
- }
14936
- if (options === null || options === void 0 ? void 0 : options.iceRestart) {
14937
- this.log.debug('restarting ICE', this.logContext);
14938
- this.restartingIce = true;
14939
- }
14940
- if (this._pc && this._pc.signalingState === 'have-local-offer') {
14941
- // we're waiting for the peer to accept our offer, so we'll just wait
14942
- // the only exception to this is when ICE restart is needed
14943
- const currentSD = this._pc.remoteDescription;
14944
- if ((options === null || options === void 0 ? void 0 : options.iceRestart) && currentSD) {
14945
- // TODO: handle when ICE restart is needed but we don't have a remote description
14946
- // the best thing to do is to recreate the peerconnection
14947
- yield this._pc.setRemoteDescription(currentSD);
14948
- } else {
14949
- 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) {
14950
14959
  return;
14951
14960
  }
14952
- } else if (!this._pc || this._pc.signalingState === 'closed') {
14953
- this.log.warn('could not createOffer with closed peer connection', this.logContext);
14954
- return;
14955
- }
14956
- // actually negotiate
14957
- this.log.debug('starting to negotiate', this.logContext);
14958
- const offer = yield this.pc.createOffer(options);
14959
- this.log.debug('original offer', Object.assign({
14960
- sdp: offer.sdp
14961
- }, this.logContext));
14962
- const sdpParsed = libExports.parse((_a = offer.sdp) !== null && _a !== void 0 ? _a : '');
14963
- sdpParsed.media.forEach(media => {
14964
- ensureIPAddrMatchVersion(media);
14965
- if (media.type === 'audio') {
14966
- ensureAudioNackAndStereo(media, [], []);
14967
- } else if (media.type === 'video') {
14968
- this.trackBitrates.some(trackbr => {
14969
- if (!media.msid || !trackbr.cid || !media.msid.includes(trackbr.cid)) {
14970
- return false;
14971
- }
14972
- let codecPayload = 0;
14973
- media.rtp.some(rtp => {
14974
- if (rtp.codec.toUpperCase() === trackbr.codec.toUpperCase()) {
14975
- 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) {
14976
15006
  return true;
14977
15007
  }
14978
- return false;
14979
- });
14980
- if (codecPayload === 0) {
14981
- return true;
14982
- }
14983
- if (isSVCCodec(trackbr.codec)) {
14984
- this.ensureVideoDDExtensionForSVC(media, sdpParsed);
14985
- }
14986
- // TODO: av1 slow starting issue already fixed in chrome 124, clean this after some versions
14987
- // mung sdp for av1 bitrate setting that can't apply by sendEncoding
14988
- if (trackbr.codec !== 'av1') {
14989
- return true;
14990
- }
14991
- const startBitrate = Math.round(trackbr.maxbr * startBitrateForSVC);
14992
- for (const fmtp of media.fmtp) {
14993
- if (fmtp.payload === codecPayload) {
14994
- // if another track's fmtp already is set, we cannot override the bitrate
14995
- // this has the unfortunate consequence of being forced to use the
14996
- // initial track's bitrate for all tracks
14997
- if (!fmtp.config.includes('x-google-start-bitrate')) {
14998
- 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;
14999
15026
  }
15000
- break;
15001
15027
  }
15002
- }
15003
- return true;
15004
- });
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;
15005
15038
  }
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;
15039
+ yield this.setMungedSDP(offer, libExports.write(sdpParsed));
15040
+ this.onOffer(offer, this.latestOfferId);
15041
+ } finally {
15042
+ unlock();
15013
15043
  }
15014
- yield this.setMungedSDP(offer, libExports.write(sdpParsed));
15015
- this.onOffer(offer, this.latestOfferId);
15016
15044
  });
15017
15045
  }
15018
15046
  createAndSetAnswer() {
@@ -16677,7 +16705,7 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
16677
16705
  // before M113.
16678
16706
  // Announced here: https://groups.google.com/g/discuss-webrtc/c/-QQ3pxrl-fw?pli=1
16679
16707
  const browser = getBrowser();
16680
- if (isSafari() ||
16708
+ if (isSafariBased() ||
16681
16709
  // Even tho RN runs M114, it does not produce SVC layers when a single encoding
16682
16710
  // is provided. So we'll use the legacy SVC specification for now.
16683
16711
  // TODO: when we upstream libwebrtc, this will need additional verification
@@ -16964,6 +16992,8 @@ class LocalVideoTrack extends LocalTrack {
16964
16992
  /* @internal */
16965
16993
  this.simulcastCodecs = new Map();
16966
16994
  this.degradationPreference = 'balanced';
16995
+ this.isCpuConstrained = false;
16996
+ this.optimizeForPerformance = false;
16967
16997
  this.monitorSender = () => __awaiter(this, void 0, void 0, function* () {
16968
16998
  if (!this.sender) {
16969
16999
  this._currentBitrate = 0;
@@ -16973,12 +17003,19 @@ class LocalVideoTrack extends LocalTrack {
16973
17003
  try {
16974
17004
  stats = yield this.getSenderStats();
16975
17005
  } catch (e) {
16976
- 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), {
16977
17007
  error: e
16978
17008
  }));
16979
17009
  return;
16980
17010
  }
16981
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
+ }
16982
17019
  if (this.prevStats) {
16983
17020
  let totalBitrate = 0;
16984
17021
  statsMap.forEach((s, key) => {
@@ -17214,6 +17251,8 @@ class LocalVideoTrack extends LocalTrack {
17214
17251
  }
17215
17252
  }
17216
17253
  yield this.restart(constraints);
17254
+ // reset cpu constrained state after track is restarted
17255
+ this.isCpuConstrained = false;
17217
17256
  try {
17218
17257
  for (var _e = true, _f = __asyncValues(this.simulcastCodecs.values()), _g; _g = yield _f.next(), _a = _g.done, !_a; _e = true) {
17219
17258
  _c = _g.value;
@@ -17383,6 +17422,12 @@ class LocalVideoTrack extends LocalTrack {
17383
17422
  */
17384
17423
  setPublishingLayers(isSvc, qualities) {
17385
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
+ }
17386
17431
  this.log.debug('setting publishing layers', Object.assign(Object.assign({}, this.logContext), {
17387
17432
  qualities
17388
17433
  }));
@@ -17392,6 +17437,44 @@ class LocalVideoTrack extends LocalTrack {
17392
17437
  yield setPublishingLayersForSender(this.sender, this.encodings, qualities, this.senderLock, isSvc, this.log, this.logContext);
17393
17438
  });
17394
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
+ }
17395
17478
  handleAppVisibilityChanged() {
17396
17479
  const _super = Object.create(null, {
17397
17480
  handleAppVisibilityChanged: {
@@ -19818,16 +19901,23 @@ class LocalTrackPublication extends TrackPublication {
19818
19901
  this.handleTrackEnded = () => {
19819
19902
  this.emit(TrackEvent.Ended);
19820
19903
  };
19904
+ this.handleCpuConstrained = () => {
19905
+ if (this.track && isVideoTrack(this.track)) {
19906
+ this.emit(TrackEvent.CpuConstrained, this.track);
19907
+ }
19908
+ };
19821
19909
  this.updateInfo(ti);
19822
19910
  this.setTrack(track);
19823
19911
  }
19824
19912
  setTrack(track) {
19825
19913
  if (this.track) {
19826
19914
  this.track.off(TrackEvent.Ended, this.handleTrackEnded);
19915
+ this.track.off(TrackEvent.CpuConstrained, this.handleCpuConstrained);
19827
19916
  }
19828
19917
  super.setTrack(track);
19829
19918
  if (track) {
19830
19919
  track.on(TrackEvent.Ended, this.handleTrackEnded);
19920
+ track.on(TrackEvent.CpuConstrained, this.handleCpuConstrained);
19831
19921
  }
19832
19922
  }
19833
19923
  get isMuted() {
@@ -20065,7 +20155,7 @@ function createLocalScreenTracks(options) {
20065
20155
  if (options === undefined) {
20066
20156
  options = {};
20067
20157
  }
20068
- if (options.resolution === undefined && !isSafari17()) {
20158
+ if (options.resolution === undefined && !isSafari17Based()) {
20069
20159
  options.resolution = ScreenSharePresets.h1080fps30.resolution;
20070
20160
  }
20071
20161
  if (navigator.mediaDevices.getDisplayMedia === undefined) {
@@ -20498,6 +20588,10 @@ class LocalParticipant extends Participant {
20498
20588
  }
20499
20589
  this.engine.client.sendUpdateLocalAudioTrack(pub.trackSid, pub.getTrackFeatures());
20500
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
+ };
20501
20595
  this.handleSubscribedQualityUpdate = update => __awaiter(this, void 0, void 0, function* () {
20502
20596
  var _a, e_1, _b, _c;
20503
20597
  var _d;
@@ -20957,7 +21051,7 @@ class LocalParticipant extends Participant {
20957
21051
  if (navigator.mediaDevices.getDisplayMedia === undefined) {
20958
21052
  throw new DeviceUnsupportedError('getDisplayMedia not supported');
20959
21053
  }
20960
- if (options.resolution === undefined && !isSafari17()) {
21054
+ if (options.resolution === undefined && !isSafari17Based()) {
20961
21055
  // we need to constrain the dimensions, otherwise it could lead to low bitrate
20962
21056
  // due to encoding a huge video. Encoding such large surfaces is really expensive
20963
21057
  // unfortunately Safari 17 has a but and cannot be constrained by default
@@ -21324,6 +21418,7 @@ class LocalParticipant extends Participant {
21324
21418
  throw new UnexpectedConnectionState('pcManager is not ready');
21325
21419
  }
21326
21420
  track.sender = yield this.engine.createSender(track, opts, encodings);
21421
+ this.emit(ParticipantEvent.LocalSenderCreated, track.sender, track);
21327
21422
  if (isLocalVideoTrack(track)) {
21328
21423
  (_a = opts.degradationPreference) !== null && _a !== void 0 ? _a : opts.degradationPreference = getDefaultDegradationPreference(track);
21329
21424
  track.setDegradationPreference(opts.degradationPreference);
@@ -21409,6 +21504,7 @@ class LocalParticipant extends Participant {
21409
21504
  loggerName: this.roomOptions.loggerName,
21410
21505
  loggerContextCb: () => this.logContext
21411
21506
  });
21507
+ publication.on(TrackEvent.CpuConstrained, constrainedTrack => this.onTrackCpuConstrained(constrainedTrack, publication));
21412
21508
  // save options for when it needs to be republished again
21413
21509
  publication.options = opts;
21414
21510
  track.sid = ti.sid;