@whereby.com/media 2.3.0 → 2.5.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.
package/dist/index.cjs CHANGED
@@ -297,7 +297,7 @@ const ADDITIONAL_SCREEN_SHARE_SETTINGS = {
297
297
  { scaleResolutionDownBy: 1, dtx: true, maxBitrate: 1500000 },
298
298
  ],
299
299
  };
300
- const getMediaSettings = (kind, isScreenShare, features, isSomeoneAlreadyPresenting = false) => {
300
+ const getMediaSettings = (kind, isScreenShare, features, areTooManyAlreadyPresenting = false) => {
301
301
  var _a;
302
302
  const { lowDataModeEnabled, simulcastScreenshareOn, vp9On } = features;
303
303
  if (kind === "audio") {
@@ -307,7 +307,7 @@ const getMediaSettings = (kind, isScreenShare, features, isSomeoneAlreadyPresent
307
307
  const isVp9Available = isChrome && vp9On;
308
308
  if (isScreenShare) {
309
309
  return getScreenShareMediaSettings({
310
- isSomeoneAlreadyPresenting,
310
+ areTooManyAlreadyPresenting,
311
311
  simulcastScreenshareOn,
312
312
  });
313
313
  }
@@ -330,8 +330,8 @@ const getCameraMediaSettings = ({ lowBandwidth, isVp9Available, }) => {
330
330
  }
331
331
  return VIDEO_SETTINGS_HD;
332
332
  };
333
- const getScreenShareMediaSettings = ({ isSomeoneAlreadyPresenting, simulcastScreenshareOn, }) => {
334
- if (isSomeoneAlreadyPresenting) {
333
+ const getScreenShareMediaSettings = ({ areTooManyAlreadyPresenting, simulcastScreenshareOn, }) => {
334
+ if (areTooManyAlreadyPresenting) {
335
335
  return ADDITIONAL_SCREEN_SHARE_SETTINGS;
336
336
  }
337
337
  if (simulcastScreenshareOn)
@@ -1258,6 +1258,8 @@ const PROTOCOL_RESPONSES = {
1258
1258
  CLIENT_UNABLE_TO_JOIN: "client_unable_to_join",
1259
1259
  LIVE_TRANSCRIPTION_STARTED: "live_transcription_started",
1260
1260
  LIVE_TRANSCRIPTION_STOPPED: "live_transcription_stopped",
1261
+ LIVE_CAPTIONS_STARTED: "live_captions_started",
1262
+ LIVE_CAPTIONS_STOPPED: "live_captions_stopped",
1261
1263
  };
1262
1264
  const PROTOCOL_ERRORS = {
1263
1265
  CANNOT_INVITE_YOURSELF: "cannot_invite_yourself",
@@ -3989,6 +3991,37 @@ function getNumberOfTemporalLayers(consumer) {
3989
3991
  var _a, _b, _c;
3990
3992
  return /T3/.test(((_c = (_b = (_a = consumer._rtpParameters) === null || _a === void 0 ? void 0 : _a.encodings) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.scalabilityMode) || "") ? 3 : 2;
3991
3993
  }
3994
+ function addProducerCpuOveruseWatch({ producer, onOveruse }) {
3995
+ var _a;
3996
+ const encodings = (_a = producer.rtpParameters) === null || _a === void 0 ? void 0 : _a.encodings;
3997
+ let interval = null;
3998
+ if ((encodings === null || encodings === void 0 ? void 0 : encodings.length) === 3) {
3999
+ const { ssrc, rid } = encodings[0];
4000
+ let maxHeight = 0;
4001
+ let ticks = 0;
4002
+ const targetTicksForTrigger = 2;
4003
+ interval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
4004
+ (yield producer.getStats()).forEach((report) => {
4005
+ if (report.type === "outbound-rtp" && ((ssrc && report.ssrc === ssrc) || (rid && report.rid === rid))) {
4006
+ if (maxHeight && report.frameHeight && report.frameHeight < maxHeight) {
4007
+ ticks++;
4008
+ if (ticks >= targetTicksForTrigger) {
4009
+ onOveruse();
4010
+ }
4011
+ }
4012
+ else {
4013
+ ticks = 0;
4014
+ }
4015
+ maxHeight = Math.max(maxHeight, report.frameHeight || 0);
4016
+ }
4017
+ });
4018
+ }), 2000);
4019
+ }
4020
+ return () => {
4021
+ if (interval)
4022
+ clearInterval(interval);
4023
+ };
4024
+ }
3992
4025
 
3993
4026
  class SfuV2Parser {
3994
4027
  static parse(raw) {
@@ -4454,6 +4487,7 @@ class VegaRtcManager {
4454
4487
  this._emitToPWA(PROTOCOL_EVENTS.MEDIA_QUALITY_CHANGED, payload);
4455
4488
  });
4456
4489
  this._networkIsDetectedUpBySignal = false;
4490
+ this._cpuOveruseDetected = false;
4457
4491
  }
4458
4492
  _updateAndScheduleMediaServersRefresh({ iceServers, turnServers, sfuServer, sfuServers, mediaserverConfigTtlSeconds, }) {
4459
4493
  var _a, _b, _c;
@@ -4945,6 +4979,23 @@ class VegaRtcManager {
4945
4979
  this._internalSetupMicScore();
4946
4980
  }
4947
4981
  }
4982
+ _syncResourceUsage() {
4983
+ var _a;
4984
+ if (this._webcamProducer) {
4985
+ const simulcastLayer3ShouldBeActive = !this._cpuOveruseDetected;
4986
+ const params = this._webcamProducer.rtpParameters;
4987
+ if (((_a = params === null || params === void 0 ? void 0 : params.encodings) === null || _a === void 0 ? void 0 : _a.length) === 3) {
4988
+ const targetMaxSpatialLayer = simulcastLayer3ShouldBeActive ? 2 : 1;
4989
+ if (this._webcamProducer.maxSpatialLayer !== targetMaxSpatialLayer) {
4990
+ this._webcamProducer.setMaxSpatialLayer(targetMaxSpatialLayer);
4991
+ rtcStats.sendEvent("simulcast_layer_activation_changed", {
4992
+ layerIndex: 2,
4993
+ active: simulcastLayer3ShouldBeActive,
4994
+ });
4995
+ }
4996
+ }
4997
+ }
4998
+ }
4948
4999
  _internalSendWebcam() {
4949
5000
  return __awaiter(this, void 0, void 0, function* () {
4950
5001
  var _a;
@@ -4967,6 +5018,17 @@ class VegaRtcManager {
4967
5018
  paused: currentPaused,
4968
5019
  } }));
4969
5020
  currentPaused ? producer.pause() : producer.resume();
5021
+ const cleanUpCpuWatch = this._features.producerCpuOveruseWatchOn
5022
+ ? addProducerCpuOveruseWatch({
5023
+ producer,
5024
+ onOveruse: () => {
5025
+ rtcStats.sendEvent("producer_cpuoveruse_detected", {});
5026
+ cleanUpCpuWatch();
5027
+ this._cpuOveruseDetected = true;
5028
+ this._syncResourceUsage();
5029
+ },
5030
+ })
5031
+ : () => { };
4970
5032
  this._webcamProducer = producer;
4971
5033
  this._qualityMonitor.addProducer(this._selfId, producer.id);
4972
5034
  producer.observer.once("close", () => {
@@ -4974,6 +5036,7 @@ class VegaRtcManager {
4974
5036
  logger$2.info('webcamProducer "close" event');
4975
5037
  if (producer.appData.localClosed)
4976
5038
  (_a = this._vegaConnection) === null || _a === void 0 ? void 0 : _a.message("closeProducers", { producerIds: [producer.id] });
5039
+ cleanUpCpuWatch();
4977
5040
  this._webcamProducer = null;
4978
5041
  this._webcamProducerPromise = null;
4979
5042
  this._qualityMonitor.removeProducer(this._selfId, producer.id);
@@ -5061,7 +5124,7 @@ class VegaRtcManager {
5061
5124
  const codec = this._features.sfuVp9On
5062
5125
  ? (_b = (_a = this._routerRtpCapabilities) === null || _a === void 0 ? void 0 : _a.codecs) === null || _b === void 0 ? void 0 : _b.find((codec) => codec.mimeType.match(/vp8/i))
5063
5126
  : undefined;
5064
- const producer = yield this._sendTransport.produce(Object.assign(Object.assign({ track: this._screenVideoTrack, disableTrackOnPause: false, stopTracks: false, codec }, getMediaSettings("video", true, Object.assign(Object.assign({}, this._features), { vp9On: false }), this._getIsSomeoneAlreadyPresenting())), { appData: {
5127
+ const producer = yield this._sendTransport.produce(Object.assign(Object.assign({ track: this._screenVideoTrack, disableTrackOnPause: false, stopTracks: false, codec }, getMediaSettings("video", true, Object.assign(Object.assign({}, this._features), { vp9On: false }), this._getAreTooManyAlreadyPresenting())), { appData: {
5065
5128
  streamId: OUTBOUND_SCREEN_OUTBOUND_STREAM_ID,
5066
5129
  sourceClientId: this._selfId,
5067
5130
  screenShare: true,
@@ -5195,8 +5258,9 @@ class VegaRtcManager {
5195
5258
  producer.appData.localClosed = true;
5196
5259
  producer.close();
5197
5260
  }
5198
- _getIsSomeoneAlreadyPresenting() {
5199
- return !![...this._clientStates.values()].find((state) => state.hasAcceptedScreenStream && state.screenStream);
5261
+ _getAreTooManyAlreadyPresenting() {
5262
+ return ([...this._clientStates.values()].filter((state) => state.hasAcceptedScreenStream && state.screenStream)
5263
+ .length >= 3);
5200
5264
  }
5201
5265
  isInitializedWith({ selfId, roomName, isSfu }) {
5202
5266
  return this._selfId === selfId && this._room.name === roomName && Boolean(isSfu);
package/dist/index.d.cts CHANGED
@@ -717,7 +717,7 @@ declare const getMediaSettings: (kind: string, isScreenShare: boolean, features:
717
717
  lowDataModeEnabled?: boolean;
718
718
  simulcastScreenshareOn?: boolean;
719
719
  vp9On?: boolean;
720
- }, isSomeoneAlreadyPresenting?: boolean) => {
720
+ }, areTooManyAlreadyPresenting?: boolean) => {
721
721
  encodings: {}[];
722
722
  };
723
723
  declare const modifyMediaCapabilities: (routerRtpCapabilities: RtpCapabilities, features: {
@@ -1109,6 +1109,8 @@ interface SignalEvents {
1109
1109
  video_enable_requested: VideoEnableRequestedEvent;
1110
1110
  live_transcription_started: LiveTranscriptionStartedEvent;
1111
1111
  live_transcription_stopped: LiveTranscriptionStoppedEvent;
1112
+ live_captions_started: void;
1113
+ live_captions_stopped: void;
1112
1114
  }
1113
1115
  interface IdentifyDeviceRequest {
1114
1116
  deviceCredentials: Credentials;
@@ -1546,6 +1548,7 @@ declare class VegaRtcManager implements RtcManager {
1546
1548
  _isConnectingOrConnected: boolean;
1547
1549
  _vegaConnectionManager?: ReturnType<typeof createVegaConnectionManager>;
1548
1550
  _networkIsDetectedUpBySignal: boolean;
1551
+ _cpuOveruseDetected: boolean;
1549
1552
  constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features, eventClaim, }: {
1550
1553
  selfId: any;
1551
1554
  room: any;
@@ -1579,6 +1582,7 @@ declare class VegaRtcManager implements RtcManager {
1579
1582
  _pauseResumeMic(): void;
1580
1583
  _sendMic(track: MediaStreamTrack): Promise<void>;
1581
1584
  _sendMicScore(score: number): void;
1585
+ _syncResourceUsage(): void;
1582
1586
  _internalSendWebcam(): Promise<void>;
1583
1587
  _replaceWebcamTrack(): Promise<void>;
1584
1588
  _pauseResumeWebcam(): void;
@@ -1590,7 +1594,7 @@ declare class VegaRtcManager implements RtcManager {
1590
1594
  _replaceScreenAudioTrack(): Promise<void>;
1591
1595
  _sendScreenAudio(track: MediaStreamTrack): Promise<void>;
1592
1596
  _stopProducer(producer: any): void;
1593
- _getIsSomeoneAlreadyPresenting(): boolean;
1597
+ _getAreTooManyAlreadyPresenting(): boolean;
1594
1598
  isInitializedWith({ selfId, roomName, isSfu }: {
1595
1599
  selfId: string;
1596
1600
  roomName: string;
@@ -1757,6 +1761,8 @@ declare const PROTOCOL_RESPONSES: {
1757
1761
  CLIENT_UNABLE_TO_JOIN: string;
1758
1762
  LIVE_TRANSCRIPTION_STARTED: string;
1759
1763
  LIVE_TRANSCRIPTION_STOPPED: string;
1764
+ LIVE_CAPTIONS_STARTED: string;
1765
+ LIVE_CAPTIONS_STOPPED: string;
1760
1766
  };
1761
1767
  declare const PROTOCOL_ERRORS: {
1762
1768
  CANNOT_INVITE_YOURSELF: string;
package/dist/index.d.mts CHANGED
@@ -717,7 +717,7 @@ declare const getMediaSettings: (kind: string, isScreenShare: boolean, features:
717
717
  lowDataModeEnabled?: boolean;
718
718
  simulcastScreenshareOn?: boolean;
719
719
  vp9On?: boolean;
720
- }, isSomeoneAlreadyPresenting?: boolean) => {
720
+ }, areTooManyAlreadyPresenting?: boolean) => {
721
721
  encodings: {}[];
722
722
  };
723
723
  declare const modifyMediaCapabilities: (routerRtpCapabilities: RtpCapabilities, features: {
@@ -1109,6 +1109,8 @@ interface SignalEvents {
1109
1109
  video_enable_requested: VideoEnableRequestedEvent;
1110
1110
  live_transcription_started: LiveTranscriptionStartedEvent;
1111
1111
  live_transcription_stopped: LiveTranscriptionStoppedEvent;
1112
+ live_captions_started: void;
1113
+ live_captions_stopped: void;
1112
1114
  }
1113
1115
  interface IdentifyDeviceRequest {
1114
1116
  deviceCredentials: Credentials;
@@ -1546,6 +1548,7 @@ declare class VegaRtcManager implements RtcManager {
1546
1548
  _isConnectingOrConnected: boolean;
1547
1549
  _vegaConnectionManager?: ReturnType<typeof createVegaConnectionManager>;
1548
1550
  _networkIsDetectedUpBySignal: boolean;
1551
+ _cpuOveruseDetected: boolean;
1549
1552
  constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features, eventClaim, }: {
1550
1553
  selfId: any;
1551
1554
  room: any;
@@ -1579,6 +1582,7 @@ declare class VegaRtcManager implements RtcManager {
1579
1582
  _pauseResumeMic(): void;
1580
1583
  _sendMic(track: MediaStreamTrack): Promise<void>;
1581
1584
  _sendMicScore(score: number): void;
1585
+ _syncResourceUsage(): void;
1582
1586
  _internalSendWebcam(): Promise<void>;
1583
1587
  _replaceWebcamTrack(): Promise<void>;
1584
1588
  _pauseResumeWebcam(): void;
@@ -1590,7 +1594,7 @@ declare class VegaRtcManager implements RtcManager {
1590
1594
  _replaceScreenAudioTrack(): Promise<void>;
1591
1595
  _sendScreenAudio(track: MediaStreamTrack): Promise<void>;
1592
1596
  _stopProducer(producer: any): void;
1593
- _getIsSomeoneAlreadyPresenting(): boolean;
1597
+ _getAreTooManyAlreadyPresenting(): boolean;
1594
1598
  isInitializedWith({ selfId, roomName, isSfu }: {
1595
1599
  selfId: string;
1596
1600
  roomName: string;
@@ -1757,6 +1761,8 @@ declare const PROTOCOL_RESPONSES: {
1757
1761
  CLIENT_UNABLE_TO_JOIN: string;
1758
1762
  LIVE_TRANSCRIPTION_STARTED: string;
1759
1763
  LIVE_TRANSCRIPTION_STOPPED: string;
1764
+ LIVE_CAPTIONS_STARTED: string;
1765
+ LIVE_CAPTIONS_STOPPED: string;
1760
1766
  };
1761
1767
  declare const PROTOCOL_ERRORS: {
1762
1768
  CANNOT_INVITE_YOURSELF: string;
package/dist/index.d.ts CHANGED
@@ -717,7 +717,7 @@ declare const getMediaSettings: (kind: string, isScreenShare: boolean, features:
717
717
  lowDataModeEnabled?: boolean;
718
718
  simulcastScreenshareOn?: boolean;
719
719
  vp9On?: boolean;
720
- }, isSomeoneAlreadyPresenting?: boolean) => {
720
+ }, areTooManyAlreadyPresenting?: boolean) => {
721
721
  encodings: {}[];
722
722
  };
723
723
  declare const modifyMediaCapabilities: (routerRtpCapabilities: RtpCapabilities, features: {
@@ -1109,6 +1109,8 @@ interface SignalEvents {
1109
1109
  video_enable_requested: VideoEnableRequestedEvent;
1110
1110
  live_transcription_started: LiveTranscriptionStartedEvent;
1111
1111
  live_transcription_stopped: LiveTranscriptionStoppedEvent;
1112
+ live_captions_started: void;
1113
+ live_captions_stopped: void;
1112
1114
  }
1113
1115
  interface IdentifyDeviceRequest {
1114
1116
  deviceCredentials: Credentials;
@@ -1546,6 +1548,7 @@ declare class VegaRtcManager implements RtcManager {
1546
1548
  _isConnectingOrConnected: boolean;
1547
1549
  _vegaConnectionManager?: ReturnType<typeof createVegaConnectionManager>;
1548
1550
  _networkIsDetectedUpBySignal: boolean;
1551
+ _cpuOveruseDetected: boolean;
1549
1552
  constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features, eventClaim, }: {
1550
1553
  selfId: any;
1551
1554
  room: any;
@@ -1579,6 +1582,7 @@ declare class VegaRtcManager implements RtcManager {
1579
1582
  _pauseResumeMic(): void;
1580
1583
  _sendMic(track: MediaStreamTrack): Promise<void>;
1581
1584
  _sendMicScore(score: number): void;
1585
+ _syncResourceUsage(): void;
1582
1586
  _internalSendWebcam(): Promise<void>;
1583
1587
  _replaceWebcamTrack(): Promise<void>;
1584
1588
  _pauseResumeWebcam(): void;
@@ -1590,7 +1594,7 @@ declare class VegaRtcManager implements RtcManager {
1590
1594
  _replaceScreenAudioTrack(): Promise<void>;
1591
1595
  _sendScreenAudio(track: MediaStreamTrack): Promise<void>;
1592
1596
  _stopProducer(producer: any): void;
1593
- _getIsSomeoneAlreadyPresenting(): boolean;
1597
+ _getAreTooManyAlreadyPresenting(): boolean;
1594
1598
  isInitializedWith({ selfId, roomName, isSfu }: {
1595
1599
  selfId: string;
1596
1600
  roomName: string;
@@ -1757,6 +1761,8 @@ declare const PROTOCOL_RESPONSES: {
1757
1761
  CLIENT_UNABLE_TO_JOIN: string;
1758
1762
  LIVE_TRANSCRIPTION_STARTED: string;
1759
1763
  LIVE_TRANSCRIPTION_STOPPED: string;
1764
+ LIVE_CAPTIONS_STARTED: string;
1765
+ LIVE_CAPTIONS_STOPPED: string;
1760
1766
  };
1761
1767
  declare const PROTOCOL_ERRORS: {
1762
1768
  CANNOT_INVITE_YOURSELF: string;
package/dist/index.mjs CHANGED
@@ -276,7 +276,7 @@ const ADDITIONAL_SCREEN_SHARE_SETTINGS = {
276
276
  { scaleResolutionDownBy: 1, dtx: true, maxBitrate: 1500000 },
277
277
  ],
278
278
  };
279
- const getMediaSettings = (kind, isScreenShare, features, isSomeoneAlreadyPresenting = false) => {
279
+ const getMediaSettings = (kind, isScreenShare, features, areTooManyAlreadyPresenting = false) => {
280
280
  var _a;
281
281
  const { lowDataModeEnabled, simulcastScreenshareOn, vp9On } = features;
282
282
  if (kind === "audio") {
@@ -286,7 +286,7 @@ const getMediaSettings = (kind, isScreenShare, features, isSomeoneAlreadyPresent
286
286
  const isVp9Available = isChrome && vp9On;
287
287
  if (isScreenShare) {
288
288
  return getScreenShareMediaSettings({
289
- isSomeoneAlreadyPresenting,
289
+ areTooManyAlreadyPresenting,
290
290
  simulcastScreenshareOn,
291
291
  });
292
292
  }
@@ -309,8 +309,8 @@ const getCameraMediaSettings = ({ lowBandwidth, isVp9Available, }) => {
309
309
  }
310
310
  return VIDEO_SETTINGS_HD;
311
311
  };
312
- const getScreenShareMediaSettings = ({ isSomeoneAlreadyPresenting, simulcastScreenshareOn, }) => {
313
- if (isSomeoneAlreadyPresenting) {
312
+ const getScreenShareMediaSettings = ({ areTooManyAlreadyPresenting, simulcastScreenshareOn, }) => {
313
+ if (areTooManyAlreadyPresenting) {
314
314
  return ADDITIONAL_SCREEN_SHARE_SETTINGS;
315
315
  }
316
316
  if (simulcastScreenshareOn)
@@ -1237,6 +1237,8 @@ const PROTOCOL_RESPONSES = {
1237
1237
  CLIENT_UNABLE_TO_JOIN: "client_unable_to_join",
1238
1238
  LIVE_TRANSCRIPTION_STARTED: "live_transcription_started",
1239
1239
  LIVE_TRANSCRIPTION_STOPPED: "live_transcription_stopped",
1240
+ LIVE_CAPTIONS_STARTED: "live_captions_started",
1241
+ LIVE_CAPTIONS_STOPPED: "live_captions_stopped",
1240
1242
  };
1241
1243
  const PROTOCOL_ERRORS = {
1242
1244
  CANNOT_INVITE_YOURSELF: "cannot_invite_yourself",
@@ -3968,6 +3970,37 @@ function getNumberOfTemporalLayers(consumer) {
3968
3970
  var _a, _b, _c;
3969
3971
  return /T3/.test(((_c = (_b = (_a = consumer._rtpParameters) === null || _a === void 0 ? void 0 : _a.encodings) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.scalabilityMode) || "") ? 3 : 2;
3970
3972
  }
3973
+ function addProducerCpuOveruseWatch({ producer, onOveruse }) {
3974
+ var _a;
3975
+ const encodings = (_a = producer.rtpParameters) === null || _a === void 0 ? void 0 : _a.encodings;
3976
+ let interval = null;
3977
+ if ((encodings === null || encodings === void 0 ? void 0 : encodings.length) === 3) {
3978
+ const { ssrc, rid } = encodings[0];
3979
+ let maxHeight = 0;
3980
+ let ticks = 0;
3981
+ const targetTicksForTrigger = 2;
3982
+ interval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
3983
+ (yield producer.getStats()).forEach((report) => {
3984
+ if (report.type === "outbound-rtp" && ((ssrc && report.ssrc === ssrc) || (rid && report.rid === rid))) {
3985
+ if (maxHeight && report.frameHeight && report.frameHeight < maxHeight) {
3986
+ ticks++;
3987
+ if (ticks >= targetTicksForTrigger) {
3988
+ onOveruse();
3989
+ }
3990
+ }
3991
+ else {
3992
+ ticks = 0;
3993
+ }
3994
+ maxHeight = Math.max(maxHeight, report.frameHeight || 0);
3995
+ }
3996
+ });
3997
+ }), 2000);
3998
+ }
3999
+ return () => {
4000
+ if (interval)
4001
+ clearInterval(interval);
4002
+ };
4003
+ }
3971
4004
 
3972
4005
  class SfuV2Parser {
3973
4006
  static parse(raw) {
@@ -4433,6 +4466,7 @@ class VegaRtcManager {
4433
4466
  this._emitToPWA(PROTOCOL_EVENTS.MEDIA_QUALITY_CHANGED, payload);
4434
4467
  });
4435
4468
  this._networkIsDetectedUpBySignal = false;
4469
+ this._cpuOveruseDetected = false;
4436
4470
  }
4437
4471
  _updateAndScheduleMediaServersRefresh({ iceServers, turnServers, sfuServer, sfuServers, mediaserverConfigTtlSeconds, }) {
4438
4472
  var _a, _b, _c;
@@ -4924,6 +4958,23 @@ class VegaRtcManager {
4924
4958
  this._internalSetupMicScore();
4925
4959
  }
4926
4960
  }
4961
+ _syncResourceUsage() {
4962
+ var _a;
4963
+ if (this._webcamProducer) {
4964
+ const simulcastLayer3ShouldBeActive = !this._cpuOveruseDetected;
4965
+ const params = this._webcamProducer.rtpParameters;
4966
+ if (((_a = params === null || params === void 0 ? void 0 : params.encodings) === null || _a === void 0 ? void 0 : _a.length) === 3) {
4967
+ const targetMaxSpatialLayer = simulcastLayer3ShouldBeActive ? 2 : 1;
4968
+ if (this._webcamProducer.maxSpatialLayer !== targetMaxSpatialLayer) {
4969
+ this._webcamProducer.setMaxSpatialLayer(targetMaxSpatialLayer);
4970
+ rtcStats.sendEvent("simulcast_layer_activation_changed", {
4971
+ layerIndex: 2,
4972
+ active: simulcastLayer3ShouldBeActive,
4973
+ });
4974
+ }
4975
+ }
4976
+ }
4977
+ }
4927
4978
  _internalSendWebcam() {
4928
4979
  return __awaiter(this, void 0, void 0, function* () {
4929
4980
  var _a;
@@ -4946,6 +4997,17 @@ class VegaRtcManager {
4946
4997
  paused: currentPaused,
4947
4998
  } }));
4948
4999
  currentPaused ? producer.pause() : producer.resume();
5000
+ const cleanUpCpuWatch = this._features.producerCpuOveruseWatchOn
5001
+ ? addProducerCpuOveruseWatch({
5002
+ producer,
5003
+ onOveruse: () => {
5004
+ rtcStats.sendEvent("producer_cpuoveruse_detected", {});
5005
+ cleanUpCpuWatch();
5006
+ this._cpuOveruseDetected = true;
5007
+ this._syncResourceUsage();
5008
+ },
5009
+ })
5010
+ : () => { };
4949
5011
  this._webcamProducer = producer;
4950
5012
  this._qualityMonitor.addProducer(this._selfId, producer.id);
4951
5013
  producer.observer.once("close", () => {
@@ -4953,6 +5015,7 @@ class VegaRtcManager {
4953
5015
  logger$2.info('webcamProducer "close" event');
4954
5016
  if (producer.appData.localClosed)
4955
5017
  (_a = this._vegaConnection) === null || _a === void 0 ? void 0 : _a.message("closeProducers", { producerIds: [producer.id] });
5018
+ cleanUpCpuWatch();
4956
5019
  this._webcamProducer = null;
4957
5020
  this._webcamProducerPromise = null;
4958
5021
  this._qualityMonitor.removeProducer(this._selfId, producer.id);
@@ -5040,7 +5103,7 @@ class VegaRtcManager {
5040
5103
  const codec = this._features.sfuVp9On
5041
5104
  ? (_b = (_a = this._routerRtpCapabilities) === null || _a === void 0 ? void 0 : _a.codecs) === null || _b === void 0 ? void 0 : _b.find((codec) => codec.mimeType.match(/vp8/i))
5042
5105
  : undefined;
5043
- const producer = yield this._sendTransport.produce(Object.assign(Object.assign({ track: this._screenVideoTrack, disableTrackOnPause: false, stopTracks: false, codec }, getMediaSettings("video", true, Object.assign(Object.assign({}, this._features), { vp9On: false }), this._getIsSomeoneAlreadyPresenting())), { appData: {
5106
+ const producer = yield this._sendTransport.produce(Object.assign(Object.assign({ track: this._screenVideoTrack, disableTrackOnPause: false, stopTracks: false, codec }, getMediaSettings("video", true, Object.assign(Object.assign({}, this._features), { vp9On: false }), this._getAreTooManyAlreadyPresenting())), { appData: {
5044
5107
  streamId: OUTBOUND_SCREEN_OUTBOUND_STREAM_ID,
5045
5108
  sourceClientId: this._selfId,
5046
5109
  screenShare: true,
@@ -5174,8 +5237,9 @@ class VegaRtcManager {
5174
5237
  producer.appData.localClosed = true;
5175
5238
  producer.close();
5176
5239
  }
5177
- _getIsSomeoneAlreadyPresenting() {
5178
- return !![...this._clientStates.values()].find((state) => state.hasAcceptedScreenStream && state.screenStream);
5240
+ _getAreTooManyAlreadyPresenting() {
5241
+ return ([...this._clientStates.values()].filter((state) => state.hasAcceptedScreenStream && state.screenStream)
5242
+ .length >= 3);
5179
5243
  }
5180
5244
  isInitializedWith({ selfId, roomName, isSfu }) {
5181
5245
  return this._selfId === selfId && this._room.name === roomName && Boolean(isSfu);
@@ -276,7 +276,7 @@ const ADDITIONAL_SCREEN_SHARE_SETTINGS = {
276
276
  { scaleResolutionDownBy: 1, dtx: true, maxBitrate: 1500000 },
277
277
  ],
278
278
  };
279
- const getMediaSettings = (kind, isScreenShare, features, isSomeoneAlreadyPresenting = false) => {
279
+ const getMediaSettings = (kind, isScreenShare, features, areTooManyAlreadyPresenting = false) => {
280
280
  var _a;
281
281
  const { lowDataModeEnabled, simulcastScreenshareOn, vp9On } = features;
282
282
  if (kind === "audio") {
@@ -286,7 +286,7 @@ const getMediaSettings = (kind, isScreenShare, features, isSomeoneAlreadyPresent
286
286
  const isVp9Available = isChrome && vp9On;
287
287
  if (isScreenShare) {
288
288
  return getScreenShareMediaSettings({
289
- isSomeoneAlreadyPresenting,
289
+ areTooManyAlreadyPresenting,
290
290
  simulcastScreenshareOn,
291
291
  });
292
292
  }
@@ -309,8 +309,8 @@ const getCameraMediaSettings = ({ lowBandwidth, isVp9Available, }) => {
309
309
  }
310
310
  return VIDEO_SETTINGS_HD;
311
311
  };
312
- const getScreenShareMediaSettings = ({ isSomeoneAlreadyPresenting, simulcastScreenshareOn, }) => {
313
- if (isSomeoneAlreadyPresenting) {
312
+ const getScreenShareMediaSettings = ({ areTooManyAlreadyPresenting, simulcastScreenshareOn, }) => {
313
+ if (areTooManyAlreadyPresenting) {
314
314
  return ADDITIONAL_SCREEN_SHARE_SETTINGS;
315
315
  }
316
316
  if (simulcastScreenshareOn)
@@ -1237,6 +1237,8 @@ const PROTOCOL_RESPONSES = {
1237
1237
  CLIENT_UNABLE_TO_JOIN: "client_unable_to_join",
1238
1238
  LIVE_TRANSCRIPTION_STARTED: "live_transcription_started",
1239
1239
  LIVE_TRANSCRIPTION_STOPPED: "live_transcription_stopped",
1240
+ LIVE_CAPTIONS_STARTED: "live_captions_started",
1241
+ LIVE_CAPTIONS_STOPPED: "live_captions_stopped",
1240
1242
  };
1241
1243
  const PROTOCOL_ERRORS = {
1242
1244
  CANNOT_INVITE_YOURSELF: "cannot_invite_yourself",
@@ -3968,6 +3970,37 @@ function getNumberOfTemporalLayers(consumer) {
3968
3970
  var _a, _b, _c;
3969
3971
  return /T3/.test(((_c = (_b = (_a = consumer._rtpParameters) === null || _a === void 0 ? void 0 : _a.encodings) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.scalabilityMode) || "") ? 3 : 2;
3970
3972
  }
3973
+ function addProducerCpuOveruseWatch({ producer, onOveruse }) {
3974
+ var _a;
3975
+ const encodings = (_a = producer.rtpParameters) === null || _a === void 0 ? void 0 : _a.encodings;
3976
+ let interval = null;
3977
+ if ((encodings === null || encodings === void 0 ? void 0 : encodings.length) === 3) {
3978
+ const { ssrc, rid } = encodings[0];
3979
+ let maxHeight = 0;
3980
+ let ticks = 0;
3981
+ const targetTicksForTrigger = 2;
3982
+ interval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
3983
+ (yield producer.getStats()).forEach((report) => {
3984
+ if (report.type === "outbound-rtp" && ((ssrc && report.ssrc === ssrc) || (rid && report.rid === rid))) {
3985
+ if (maxHeight && report.frameHeight && report.frameHeight < maxHeight) {
3986
+ ticks++;
3987
+ if (ticks >= targetTicksForTrigger) {
3988
+ onOveruse();
3989
+ }
3990
+ }
3991
+ else {
3992
+ ticks = 0;
3993
+ }
3994
+ maxHeight = Math.max(maxHeight, report.frameHeight || 0);
3995
+ }
3996
+ });
3997
+ }), 2000);
3998
+ }
3999
+ return () => {
4000
+ if (interval)
4001
+ clearInterval(interval);
4002
+ };
4003
+ }
3971
4004
 
3972
4005
  class SfuV2Parser {
3973
4006
  static parse(raw) {
@@ -4433,6 +4466,7 @@ class VegaRtcManager {
4433
4466
  this._emitToPWA(PROTOCOL_EVENTS.MEDIA_QUALITY_CHANGED, payload);
4434
4467
  });
4435
4468
  this._networkIsDetectedUpBySignal = false;
4469
+ this._cpuOveruseDetected = false;
4436
4470
  }
4437
4471
  _updateAndScheduleMediaServersRefresh({ iceServers, turnServers, sfuServer, sfuServers, mediaserverConfigTtlSeconds, }) {
4438
4472
  var _a, _b, _c;
@@ -4924,6 +4958,23 @@ class VegaRtcManager {
4924
4958
  this._internalSetupMicScore();
4925
4959
  }
4926
4960
  }
4961
+ _syncResourceUsage() {
4962
+ var _a;
4963
+ if (this._webcamProducer) {
4964
+ const simulcastLayer3ShouldBeActive = !this._cpuOveruseDetected;
4965
+ const params = this._webcamProducer.rtpParameters;
4966
+ if (((_a = params === null || params === void 0 ? void 0 : params.encodings) === null || _a === void 0 ? void 0 : _a.length) === 3) {
4967
+ const targetMaxSpatialLayer = simulcastLayer3ShouldBeActive ? 2 : 1;
4968
+ if (this._webcamProducer.maxSpatialLayer !== targetMaxSpatialLayer) {
4969
+ this._webcamProducer.setMaxSpatialLayer(targetMaxSpatialLayer);
4970
+ rtcStats.sendEvent("simulcast_layer_activation_changed", {
4971
+ layerIndex: 2,
4972
+ active: simulcastLayer3ShouldBeActive,
4973
+ });
4974
+ }
4975
+ }
4976
+ }
4977
+ }
4927
4978
  _internalSendWebcam() {
4928
4979
  return __awaiter(this, void 0, void 0, function* () {
4929
4980
  var _a;
@@ -4946,6 +4997,17 @@ class VegaRtcManager {
4946
4997
  paused: currentPaused,
4947
4998
  } }));
4948
4999
  currentPaused ? producer.pause() : producer.resume();
5000
+ const cleanUpCpuWatch = this._features.producerCpuOveruseWatchOn
5001
+ ? addProducerCpuOveruseWatch({
5002
+ producer,
5003
+ onOveruse: () => {
5004
+ rtcStats.sendEvent("producer_cpuoveruse_detected", {});
5005
+ cleanUpCpuWatch();
5006
+ this._cpuOveruseDetected = true;
5007
+ this._syncResourceUsage();
5008
+ },
5009
+ })
5010
+ : () => { };
4949
5011
  this._webcamProducer = producer;
4950
5012
  this._qualityMonitor.addProducer(this._selfId, producer.id);
4951
5013
  producer.observer.once("close", () => {
@@ -4953,6 +5015,7 @@ class VegaRtcManager {
4953
5015
  logger$2.info('webcamProducer "close" event');
4954
5016
  if (producer.appData.localClosed)
4955
5017
  (_a = this._vegaConnection) === null || _a === void 0 ? void 0 : _a.message("closeProducers", { producerIds: [producer.id] });
5018
+ cleanUpCpuWatch();
4956
5019
  this._webcamProducer = null;
4957
5020
  this._webcamProducerPromise = null;
4958
5021
  this._qualityMonitor.removeProducer(this._selfId, producer.id);
@@ -5040,7 +5103,7 @@ class VegaRtcManager {
5040
5103
  const codec = this._features.sfuVp9On
5041
5104
  ? (_b = (_a = this._routerRtpCapabilities) === null || _a === void 0 ? void 0 : _a.codecs) === null || _b === void 0 ? void 0 : _b.find((codec) => codec.mimeType.match(/vp8/i))
5042
5105
  : undefined;
5043
- const producer = yield this._sendTransport.produce(Object.assign(Object.assign({ track: this._screenVideoTrack, disableTrackOnPause: false, stopTracks: false, codec }, getMediaSettings("video", true, Object.assign(Object.assign({}, this._features), { vp9On: false }), this._getIsSomeoneAlreadyPresenting())), { appData: {
5106
+ const producer = yield this._sendTransport.produce(Object.assign(Object.assign({ track: this._screenVideoTrack, disableTrackOnPause: false, stopTracks: false, codec }, getMediaSettings("video", true, Object.assign(Object.assign({}, this._features), { vp9On: false }), this._getAreTooManyAlreadyPresenting())), { appData: {
5044
5107
  streamId: OUTBOUND_SCREEN_OUTBOUND_STREAM_ID,
5045
5108
  sourceClientId: this._selfId,
5046
5109
  screenShare: true,
@@ -5174,8 +5237,9 @@ class VegaRtcManager {
5174
5237
  producer.appData.localClosed = true;
5175
5238
  producer.close();
5176
5239
  }
5177
- _getIsSomeoneAlreadyPresenting() {
5178
- return !![...this._clientStates.values()].find((state) => state.hasAcceptedScreenStream && state.screenStream);
5240
+ _getAreTooManyAlreadyPresenting() {
5241
+ return ([...this._clientStates.values()].filter((state) => state.hasAcceptedScreenStream && state.screenStream)
5242
+ .length >= 3);
5179
5243
  }
5180
5244
  isInitializedWith({ selfId, roomName, isSfu }) {
5181
5245
  return this._selfId === selfId && this._room.name === roomName && Boolean(isSfu);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@whereby.com/media",
3
3
  "description": "Media library for Whereby",
4
- "version": "2.3.0",
4
+ "version": "2.5.0",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/whereby/sdk",
7
7
  "repository": {