@whereby.com/media 9.2.7 → 9.3.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.
package/dist/index.cjs CHANGED
@@ -221,9 +221,22 @@ function rtcStatsConnection(wsURL, logger = console) {
221
221
  return connection;
222
222
  }
223
223
  const RTCSTATS_URL = "wss://rtcstats.srv.whereby.com";
224
- const server = rtcStatsConnection(RTCSTATS_URL);
225
- const stats = rtcstats(server.trace, 10000, [""]);
226
- resetDelta = (stats === null || stats === void 0 ? void 0 : stats.resetDelta) || noop;
224
+ const noopServer = {
225
+ connected: false,
226
+ attemptedConnectedAtLeastOnce: false,
227
+ trace: noop,
228
+ close: noop,
229
+ connect: noop,
230
+ };
231
+ let server;
232
+ if (typeof window !== "undefined") {
233
+ server = rtcStatsConnection(RTCSTATS_URL);
234
+ const stats = rtcstats(server.trace, 10000, [""]);
235
+ resetDelta = (stats === null || stats === void 0 ? void 0 : stats.resetDelta) || noop;
236
+ }
237
+ else {
238
+ server = noopServer;
239
+ }
227
240
  const rtcStats = {
228
241
  sendEvent: (type, value) => {
229
242
  server.trace("customEvent", null, {
@@ -240,7 +253,7 @@ const rtcStats = {
240
253
  server,
241
254
  };
242
255
 
243
- const debugOn = new URLSearchParams(window.location.search).has("debug");
256
+ const debugOn = (typeof window !== "undefined" && new URLSearchParams(window.location.search).has("debug"));
244
257
  class Logger {
245
258
  constructor() {
246
259
  this._isEnabled = false;
@@ -1442,6 +1455,8 @@ function captureVideoSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, t
1442
1455
  const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
1443
1456
  ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
1444
1457
  ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
1458
+ const freezeCountDiff = (currentSsrcStats.freezeCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.freezeCount) || 0);
1459
+ ssrcMetrics.freezeRate = (freezeCountDiff * 1000) / timeDiff;
1445
1460
  }
1446
1461
  else {
1447
1462
  const kfCountDiff = currentSsrcStats.keyFramesEncoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.keyFramesEncoded) || 0);
@@ -1471,7 +1486,7 @@ const peerConnectionData = new WeakMap();
1471
1486
  const removePeerConnection = (pc) => {
1472
1487
  peerConnections = peerConnections.filter((old) => old !== pc);
1473
1488
  };
1474
- if (window.RTCPeerConnection) {
1489
+ if (typeof window !== "undefined" && window.RTCPeerConnection) {
1475
1490
  const OriginalRTCPeerConnection = window.RTCPeerConnection;
1476
1491
  function PatchedRTCPeerConnection(rtcConfig) {
1477
1492
  const pc = new OriginalRTCPeerConnection(rtcConfig);
@@ -1587,15 +1602,17 @@ const getOrCreateSsrcMetricsContainer = (statsByView, time, pcIndex, clientId, t
1587
1602
  ssrcStats.updated = time;
1588
1603
  return ssrcStats;
1589
1604
  };
1590
- const removeNonUpdatedStats = (statsByView, time) => {
1605
+ const removeNonUpdatedStats = (statsByView, time, renderedDimensionsByTrack) => {
1591
1606
  Object.entries(statsByView).forEach(([viewId, viewStats]) => {
1592
1607
  if (viewStats.updated !== undefined && viewStats.updated < time) {
1608
+ Object.keys(viewStats.tracks).forEach((trackId) => delete renderedDimensionsByTrack[trackId]);
1593
1609
  delete statsByView[viewId];
1594
1610
  }
1595
1611
  else {
1596
1612
  Object.entries(viewStats.tracks).forEach(([trackId, trackStats]) => {
1597
1613
  if (trackStats.updated < time) {
1598
1614
  delete viewStats.tracks[trackId];
1615
+ delete renderedDimensionsByTrack[trackId];
1599
1616
  }
1600
1617
  else {
1601
1618
  Object.entries(trackStats.ssrcs).forEach(([ssrc, ssrcStats]) => {
@@ -1633,7 +1650,7 @@ function collectStats(state_1, _a, immediate_1) {
1633
1650
  if (timeSinceLastUpdate < 400) {
1634
1651
  if (immediate)
1635
1652
  return state.statsByView;
1636
- state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
1653
+ state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients, state.renderedDimensionsByTrack); });
1637
1654
  state.nextTimeout = setTimeout(collectStatsBound, interval);
1638
1655
  return;
1639
1656
  }
@@ -1726,7 +1743,7 @@ function collectStats(state_1, _a, immediate_1) {
1726
1743
  }
1727
1744
  });
1728
1745
  });
1729
- removeNonUpdatedStats(state.statsByView, state.lastUpdateTime);
1746
+ removeNonUpdatedStats(state.statsByView, state.lastUpdateTime, state.renderedDimensionsByTrack);
1730
1747
  Object.entries((defaultViewStats === null || defaultViewStats === void 0 ? void 0 : defaultViewStats.candidatePairs) || {}).forEach(([cpKey, cp]) => {
1731
1748
  const active = cp.lastRtcStatsTime === state.lastUpdateTime;
1732
1749
  cp.active = active;
@@ -1745,7 +1762,7 @@ function collectStats(state_1, _a, immediate_1) {
1745
1762
  return state.statsByView;
1746
1763
  }
1747
1764
  else {
1748
- state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
1765
+ state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients, state.renderedDimensionsByTrack); });
1749
1766
  }
1750
1767
  }
1751
1768
  catch (e) {
@@ -1784,6 +1801,7 @@ const STATE = {
1784
1801
  currentMonitor: null,
1785
1802
  getClients: () => [],
1786
1803
  lastUpdateTime: 0,
1804
+ renderedDimensionsByTrack: {},
1787
1805
  statsByView: {},
1788
1806
  subscriptions: [],
1789
1807
  numFailedStatsReports: 0,
@@ -1802,6 +1820,9 @@ const getNumMissingTrackSsrcLookups = () => numMissingTrackSsrcLookups;
1802
1820
  const getNumFailedTrackSsrcLookups = () => numFailedTrackSsrcLookups;
1803
1821
  const getUpdatedStats = () => { var _a; return (_a = STATE.currentMonitor) === null || _a === void 0 ? void 0 : _a.getUpdatedStats(); };
1804
1822
  const setClientProvider = (provider) => (STATE.getClients = provider);
1823
+ const updateRenderedDimensions = (trackId, data) => {
1824
+ STATE.renderedDimensionsByTrack[trackId] = data;
1825
+ };
1805
1826
  function startStatsMonitor(state, { interval, logger }) {
1806
1827
  const collectStatsBound = collectStats.bind(null, state, { interval, logger });
1807
1828
  let cpuObserver;
@@ -2423,7 +2444,7 @@ function fromLocation({ host = "whereby.com", protocol = "https:" } = {}) {
2423
2444
  subdomain,
2424
2445
  };
2425
2446
  }
2426
- fromLocation(window && window.location);
2447
+ fromLocation(typeof window !== "undefined" ? window.location : undefined);
2427
2448
 
2428
2449
  var _a$2, _b$1;
2429
2450
  const adapter$2 = (_a$2 = adapterRaw.default) !== null && _a$2 !== void 0 ? _a$2 : adapterRaw;
@@ -2522,6 +2543,7 @@ class P2pRtcManager {
2522
2543
  this._features = features || {};
2523
2544
  this._isAudioOnlyMode = false;
2524
2545
  this._closed = false;
2546
+ this._videoTrackIdByStreamId = {};
2525
2547
  this._fetchMediaServersTimer = null;
2526
2548
  this._stopCameraTimeout = null;
2527
2549
  this._icePublicIPGatheringTimeoutID = null;
@@ -3002,6 +3024,8 @@ class P2pRtcManager {
3002
3024
  });
3003
3025
  return;
3004
3026
  }
3027
+ if (event.track.kind === "video")
3028
+ this._videoTrackIdByStreamId[stream.id] = event.track.id;
3005
3029
  if (session.streamIds.indexOf(stream.id) === -1) {
3006
3030
  session.streamIds.push(stream.id);
3007
3031
  this._emit(EVENTS.STREAM_ADDED, {
@@ -3432,7 +3456,11 @@ class P2pRtcManager {
3432
3456
  }, 60 * 1000);
3433
3457
  }
3434
3458
  }
3435
- updateStreamResolution() { }
3459
+ updateStreamResolution(streamId, _ignored, { width, height, }) {
3460
+ const trackId = this._videoTrackIdByStreamId[streamId];
3461
+ if (trackId)
3462
+ updateRenderedDimensions(trackId, { width, height, time: Date.now() });
3463
+ }
3436
3464
  stopOrResumeAudio() {
3437
3465
  }
3438
3466
  _handleStopOrResumeVideo({ enable, track }) {
@@ -5573,7 +5601,7 @@ class VegaRtcManager {
5573
5601
  this._syncIncomingStreamsWithPWA(clientId);
5574
5602
  }
5575
5603
  updateStreamResolution(streamId, _ignored, { width, height, }) {
5576
- var _a, _b;
5604
+ var _a, _b, _c;
5577
5605
  logger$2.info("updateStreamResolution()", { streamId, width, height });
5578
5606
  const consumerId = this._streamIdToVideoConsumerId.get(streamId);
5579
5607
  const consumer = this._consumers.get(consumerId);
@@ -5581,17 +5609,18 @@ class VegaRtcManager {
5581
5609
  this._streamIdToVideoResolution.set(streamId, { width, height });
5582
5610
  return;
5583
5611
  }
5612
+ updateRenderedDimensions((_a = consumer.track) === null || _a === void 0 ? void 0 : _a.id, { width, height, time: Date.now() });
5584
5613
  const numberOfActiveVideos = getNumberOfActiveVideos(this._consumers);
5585
5614
  const numberOfTemporalLayers = getNumberOfTemporalLayers(consumer);
5586
5615
  const { spatialLayer, temporalLayer } = getLayers({ width, height }, {
5587
5616
  numberOfActiveVideos,
5588
5617
  numberOfTemporalLayers,
5589
- uncappedSingleRemoteVideoOn: (_a = this._features) === null || _a === void 0 ? void 0 : _a.uncappedSingleRemoteVideoOn,
5618
+ uncappedSingleRemoteVideoOn: (_b = this._features) === null || _b === void 0 ? void 0 : _b.uncappedSingleRemoteVideoOn,
5590
5619
  });
5591
5620
  if (consumer.appData.spatialLayer !== spatialLayer || consumer.appData.temporalLayer !== temporalLayer) {
5592
5621
  consumer.appData.spatialLayer = spatialLayer;
5593
5622
  consumer.appData.temporalLayer = temporalLayer;
5594
- (_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.message("setConsumersPreferredLayers", {
5623
+ (_c = this._vegaConnection) === null || _c === void 0 ? void 0 : _c.message("setConsumersPreferredLayers", {
5595
5624
  consumerIds: [consumerId],
5596
5625
  spatialLayer,
5597
5626
  temporalLayer,
@@ -6006,6 +6035,92 @@ const getRoomMode = () => {
6006
6035
  return roomMode;
6007
6036
  };
6008
6037
 
6038
+ const createQualityDetector = (type) => {
6039
+ return {
6040
+ id: `quality-${type}`,
6041
+ enabled: ({ client }) => !client.isLocalClient && !client.isPresentation,
6042
+ check: ({ ssrc0, renderedDimensions, hasLiveTrack, kind }) => {
6043
+ if (!hasLiveTrack)
6044
+ return true;
6045
+ if (!ssrc0)
6046
+ return true;
6047
+ if (!ssrc0.bitrate)
6048
+ return true;
6049
+ if (kind === "video") {
6050
+ if (renderedDimensions && renderedDimensions.width && renderedDimensions.height) {
6051
+ const maxSideRendered = Math.max(renderedDimensions.width, renderedDimensions.height);
6052
+ const maxSideReceived = Math.max(ssrc0.width || 0, ssrc0.height || 0);
6053
+ const fpsReceived = ssrc0.fps || 0;
6054
+ const hadFreeze = !!ssrc0.freezeRate;
6055
+ const bitrate = ssrc0.bitrate || 0;
6056
+ if (maxSideRendered < 100) {
6057
+ if (type === "warning" && maxSideReceived < 160)
6058
+ return true;
6059
+ if (type === "critical" && maxSideReceived < 90)
6060
+ return true;
6061
+ if (type === "warning" && fpsReceived < 5)
6062
+ return true;
6063
+ if (type === "critical" && fpsReceived < 2)
6064
+ return true;
6065
+ if (type === "warning" && bitrate < 10000)
6066
+ return true;
6067
+ if (type === "critical" && bitrate < 5000)
6068
+ return true;
6069
+ }
6070
+ else if (maxSideRendered < 480) {
6071
+ if (type === "warning" && maxSideReceived < 240)
6072
+ return true;
6073
+ if (type === "critical" && maxSideReceived < 120)
6074
+ return true;
6075
+ if (type === "warning" && fpsReceived < 14)
6076
+ return true;
6077
+ if (type === "critical" && fpsReceived < 9)
6078
+ return true;
6079
+ if (hadFreeze)
6080
+ return true;
6081
+ if (type === "warning" && bitrate < 50000)
6082
+ return true;
6083
+ if (type === "critical" && bitrate < 20000)
6084
+ return true;
6085
+ }
6086
+ else {
6087
+ if (type === "warning" && maxSideReceived < 480)
6088
+ return true;
6089
+ if (type === "critical" && maxSideReceived < 240)
6090
+ return true;
6091
+ if (type === "warning" && fpsReceived < 14)
6092
+ return true;
6093
+ if (type === "critical" && fpsReceived < 9)
6094
+ return true;
6095
+ if (type === "warning" && bitrate < 200000)
6096
+ return true;
6097
+ if (type === "critical" && bitrate < 50000)
6098
+ return true;
6099
+ if (hadFreeze)
6100
+ return true;
6101
+ }
6102
+ }
6103
+ }
6104
+ else if (kind === "audio") {
6105
+ const audioLevel = ssrc0.audioLevel || 0;
6106
+ const concealment = ssrc0.audioConcealment || 0;
6107
+ const acceleration = ssrc0.audioAcceleration || 0;
6108
+ const deceleration = ssrc0.audioDeceleration || 0;
6109
+ const audioDistortion = concealment + acceleration + deceleration;
6110
+ if (audioLevel >= 0.01) {
6111
+ if (type === "warning" && audioDistortion > 0.1)
6112
+ return true;
6113
+ if (type === "critical" && audioDistortion > 0.2)
6114
+ return true;
6115
+ }
6116
+ }
6117
+ return false;
6118
+ },
6119
+ };
6120
+ };
6121
+ const qualityWarningDetector = createQualityDetector("warning");
6122
+ const qualityCriticalDetector = createQualityDetector("critical");
6123
+
6009
6124
  const badNetworkIssueDetector = {
6010
6125
  id: "bad-network",
6011
6126
  enabled: ({ hasLiveTrack, ssrcs }) => hasLiveTrack && ssrcs.length > 0,
@@ -6229,6 +6344,8 @@ const issueDetectors = [
6229
6344
  (ssrc0.audioLevel || 0) >= 0.001 &&
6230
6345
  (ssrc0.audioAcceleration || 0) >= 0.1,
6231
6346
  },
6347
+ qualityWarningDetector,
6348
+ qualityCriticalDetector,
6232
6349
  ];
6233
6350
 
6234
6351
  let subscriptions = [];
@@ -6383,7 +6500,7 @@ function issueDetectorOrMetricEnabled(issueDetectorOrMetric, checkData) {
6383
6500
  }
6384
6501
  return enabled && (issueDetectorOrMetric.enabled ? issueDetectorOrMetric.enabled(checkData) : true);
6385
6502
  }
6386
- function onUpdatedStats(statsByView, clients) {
6503
+ function onUpdatedStats(statsByView, clients, renderedDimensionsByTrack) {
6387
6504
  Object.values(aggregatedMetrics).forEach((metricData) => {
6388
6505
  metricData.curTicks = 0;
6389
6506
  metricData.curSum = 0;
@@ -6421,6 +6538,7 @@ function onUpdatedStats(statsByView, clients) {
6421
6538
  kind,
6422
6539
  track,
6423
6540
  trackStats,
6541
+ renderedDimensions: (track === null || track === void 0 ? void 0 : track.id) ? renderedDimensionsByTrack[track.id] : undefined,
6424
6542
  stats,
6425
6543
  hasLiveTrack,
6426
6544
  ssrc0,
@@ -7063,7 +7181,7 @@ class BandwidthTester extends EventEmitter {
7063
7181
  }
7064
7182
 
7065
7183
  const logger = new Logger();
7066
- const isMobile = /mobi/i.test(navigator.userAgent);
7184
+ const isMobile = typeof navigator !== "undefined" && /mobi/i.test(navigator.userAgent);
7067
7185
  class NoDevicesError extends Error {
7068
7186
  constructor(...args) {
7069
7187
  super(...args);
@@ -7321,8 +7439,8 @@ function hasGetDisplayMedia() {
7321
7439
  }
7322
7440
  const defaultDisplayMediaConstraints = {
7323
7441
  video: {
7324
- width: { max: window.screen.width },
7325
- height: { max: window.screen.height },
7442
+ width: { max: typeof window !== "undefined" ? window.screen.width : 0 },
7443
+ height: { max: typeof window !== "undefined" ? window.screen.height : 0 },
7326
7444
  },
7327
7445
  };
7328
7446
  function getDisplayMedia(constraints = defaultDisplayMediaConstraints, contentHint = "detail") {
@@ -7513,5 +7631,6 @@ exports.subscribeIssues = subscribeIssues;
7513
7631
  exports.subscribeStats = subscribeStats;
7514
7632
  exports.trackAnnotations = trackAnnotations;
7515
7633
  exports.turnServerOverride = turnServerOverride;
7634
+ exports.updateRenderedDimensions = updateRenderedDimensions;
7516
7635
  exports.variance = variance;
7517
7636
  //# sourceMappingURL=index.cjs.map
package/dist/index.d.cts CHANGED
@@ -27,10 +27,16 @@ type PressureRecord = {
27
27
  state: string;
28
28
  time: number;
29
29
  };
30
+ type RenderedDimensionsReport = {
31
+ time: number;
32
+ width: number;
33
+ height: number;
34
+ };
30
35
  interface TrackStats {
31
36
  startTime: number;
32
37
  updated: number;
33
38
  ssrcs: Record<string, SsrcStats>;
39
+ renderedDimensions?: RenderedDimensionsReport;
34
40
  }
35
41
  interface ViewStats {
36
42
  startTime?: number;
@@ -93,6 +99,7 @@ interface SsrcStats {
93
99
  encodeTime?: number;
94
100
  sourceWidth?: number;
95
101
  sourceFps?: number;
102
+ freezeRate?: number;
96
103
  }
97
104
 
98
105
  interface IssueDetector {
@@ -106,6 +113,7 @@ interface IssueCheckData {
106
113
  clients: StatsClient[];
107
114
  kind: string;
108
115
  track: MediaStreamTrack | MediaStreamTrackWithDenoiserContext | undefined;
116
+ renderedDimensions?: RenderedDimensionsReport;
109
117
  trackStats?: TrackStats;
110
118
  stats?: ViewStats;
111
119
  hasLiveTrack: boolean;
@@ -236,7 +244,7 @@ interface StatsMonitor {
236
244
  stop: () => void;
237
245
  }
238
246
  interface StatsSubscription {
239
- onUpdatedStats: (statsByView: Record<string, ViewStats>, clients: StatsClient[]) => void;
247
+ onUpdatedStats: (statsByView: Record<string, ViewStats>, clients: StatsClient[], renderedDimensionsByTrack: Record<string, RenderedDimensionsReport>) => void;
240
248
  }
241
249
  interface StatsMonitorState {
242
250
  currentMonitor: StatsMonitor | null;
@@ -245,6 +253,7 @@ interface StatsMonitorState {
245
253
  lastUpdateTime: number;
246
254
  nextTimeout?: number;
247
255
  pressureObserver?: PressureObserver;
256
+ renderedDimensionsByTrack: Record<string, RenderedDimensionsReport>;
248
257
  statsByView: Record<string, ViewStats>;
249
258
  subscriptions: StatsSubscription[];
250
259
  numFailedStatsReports: number;
@@ -261,6 +270,7 @@ declare const getNumMissingTrackSsrcLookups: () => number;
261
270
  declare const getNumFailedTrackSsrcLookups: () => number;
262
271
  declare const getUpdatedStats: () => Promise<Record<string, ViewStats> | undefined> | undefined;
263
272
  declare const setClientProvider: (provider: () => StatsClient[]) => () => StatsClient[];
273
+ declare const updateRenderedDimensions: (trackId: string, data: RenderedDimensionsReport) => void;
264
274
  declare function subscribeStats(subscription: StatsSubscription, options?: StatsMonitorOptions, state?: StatsMonitorState): {
265
275
  stop(): void;
266
276
  };
@@ -1641,6 +1651,7 @@ declare class P2pRtcManager implements RtcManager {
1641
1651
  analytics: P2PAnalytics;
1642
1652
  _rtcStatsDisconnectTimeout?: ReturnType<typeof setTimeout>;
1643
1653
  _webcamPaused?: boolean;
1654
+ _videoTrackIdByStreamId: Record<string, string>;
1644
1655
  constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features }: RtcManagerOptions);
1645
1656
  numberOfPeerconnections(): number;
1646
1657
  isInitializedWith({ selfId, roomName, isSfu }: {
@@ -1693,7 +1704,10 @@ declare class P2pRtcManager implements RtcManager {
1693
1704
  clientId: string;
1694
1705
  }): Session;
1695
1706
  disconnect(clientId: string): void;
1696
- updateStreamResolution(): void;
1707
+ updateStreamResolution(streamId: string, _ignored: any, { width, height, }: {
1708
+ width: number;
1709
+ height: number;
1710
+ }): void;
1697
1711
  stopOrResumeAudio(): void;
1698
1712
  _handleStopOrResumeVideo({ enable, track }: {
1699
1713
  enable: boolean;
@@ -1745,6 +1759,12 @@ declare const rtcStats: {
1745
1759
  trace: (...args: any) => void;
1746
1760
  close: () => void;
1747
1761
  connect: () => void;
1762
+ } | {
1763
+ connected: boolean;
1764
+ attemptedConnectedAtLeastOnce: boolean;
1765
+ trace: () => void;
1766
+ close: () => void;
1767
+ connect: () => void;
1748
1768
  };
1749
1769
  };
1750
1770
 
@@ -2089,5 +2109,5 @@ declare class VegaRtcManager implements RtcManager {
2089
2109
  hasClient(clientId: string): boolean;
2090
2110
  }
2091
2111
 
2092
- export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, _default as rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, variance };
2112
+ export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, _default as rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, updateRenderedDimensions, variance };
2093
2113
  export type { AddCameraStreamOptions, AddSpotlightRequest, AudioEnableRequest, AudioEnableRequestedEvent, AudioEnabledEvent, BreakoutConfig, BreakoutGroupJoinedEvent, BreakoutSessionUpdatedEvent, BuildDeviceListOptions, CannotJoinUnclaimedRoomError, ChatFileShare, ChatMessage, ChatMessageError, ChatMessageRemoved, ChatMessageRequest, ClearableTimeout, ClientKickedEvent, ClientLeftEvent, ClientMetadataPayload, ClientMetadataReceivedEvent, ClientRole, ClientUnableToJoinEvent, CloudRecordingStartedEvent, Codec, ConnectionStatus, Credentials, FileShareErrorCode, FileUploadUrl, ForbiddenError, ForbiddenErrorNames, GetConstraintsOptions, GetDeviceDataResult, GetMediaConstraintsOptions, GetStreamOptions, GetStreamResult, GetUpdatedDevicesResult, GetUserMediaAttempt, GetUserMediaAttemptOutcome, HostPresenceControlsError, IdentifyDeviceRequest, InternalServerError, InvalidAssistantKeyError, IssuesAndMetricsByView, JoinRoomRequest, KnockAcceptedEvent, KnockRejectedEvent, KnockRoomRequest, KnockerLeftEvent, LiveCaptionEvent, LiveCaptionsStartedEvent, LiveCaptionsStoppedEvent, LiveTranscriptionStartedEvent, LiveTranscriptionStoppedEvent, MaxViewerLimitReachedError, MediaPrefs, Metric, NewClientEvent, OrganizationAssistantNotEnabledError, OrganizationAssistantNotFoundError, OrganizationPlanExhaustedError, RemoveScreenshareStreamOptions, RemoveSpotlightRequest, RoleName, RoomConcurrencyControlsError, RoomEmptyError, RoomFullError, RoomJoinPermissionDeniedError, RoomJoinedErrors, RoomJoinedEvent, RoomJoinedSuccess, RoomKnockedEvent, RoomLockedError, RoomLockedEvent, RoomMeetingTimeExhaustedError, RoomMode, RoomSessionEndedEvent, RtcClientConnectionStatusChangedPayload, RtcEventEmitter, RtcEvents, RtcLocalStreamTrackAddedPayload, RtcLocalStreamTrackRemovedPayload, RtcManager, RtcManagerCreatedPayload, RtcManagerOptions, RtcStreamAddedPayload, ScreenshareStartedEvent, ScreenshareStoppedEvent, SendClientMetadataRequest, SignalClient, SignalEvents, SignalIceCandidateMessage, SignalIceEndOfCandidatesMessage, SignalIceServer, SignalKnocker, SignalMediaServerConfig, SignalRTCSessionDescription, SignalReadyToReceiveOfferMessage, SignalRequests, SignalRoom, SignalSDPMessage, SignalSFUServer, SignalTurnServer, SocketConf, SocketManager, Spotlight, SpotlightAddedEvent, SpotlightRemovedEvent, StatsMonitorOptions, StatsMonitorState, StatsSubscription, TurnTransportProtocol, UniqueRoleAlreadyInRoomError, UpdatedDeviceInfo, UpdatedDevicesInfo, VegaConnectionOptions, VegaRtcManagerOptions, VideoEnableRequest, VideoEnableRequestedEvent, VideoEnabledEvent, WebRTCProvider };
package/dist/index.d.mts CHANGED
@@ -27,10 +27,16 @@ type PressureRecord = {
27
27
  state: string;
28
28
  time: number;
29
29
  };
30
+ type RenderedDimensionsReport = {
31
+ time: number;
32
+ width: number;
33
+ height: number;
34
+ };
30
35
  interface TrackStats {
31
36
  startTime: number;
32
37
  updated: number;
33
38
  ssrcs: Record<string, SsrcStats>;
39
+ renderedDimensions?: RenderedDimensionsReport;
34
40
  }
35
41
  interface ViewStats {
36
42
  startTime?: number;
@@ -93,6 +99,7 @@ interface SsrcStats {
93
99
  encodeTime?: number;
94
100
  sourceWidth?: number;
95
101
  sourceFps?: number;
102
+ freezeRate?: number;
96
103
  }
97
104
 
98
105
  interface IssueDetector {
@@ -106,6 +113,7 @@ interface IssueCheckData {
106
113
  clients: StatsClient[];
107
114
  kind: string;
108
115
  track: MediaStreamTrack | MediaStreamTrackWithDenoiserContext | undefined;
116
+ renderedDimensions?: RenderedDimensionsReport;
109
117
  trackStats?: TrackStats;
110
118
  stats?: ViewStats;
111
119
  hasLiveTrack: boolean;
@@ -236,7 +244,7 @@ interface StatsMonitor {
236
244
  stop: () => void;
237
245
  }
238
246
  interface StatsSubscription {
239
- onUpdatedStats: (statsByView: Record<string, ViewStats>, clients: StatsClient[]) => void;
247
+ onUpdatedStats: (statsByView: Record<string, ViewStats>, clients: StatsClient[], renderedDimensionsByTrack: Record<string, RenderedDimensionsReport>) => void;
240
248
  }
241
249
  interface StatsMonitorState {
242
250
  currentMonitor: StatsMonitor | null;
@@ -245,6 +253,7 @@ interface StatsMonitorState {
245
253
  lastUpdateTime: number;
246
254
  nextTimeout?: number;
247
255
  pressureObserver?: PressureObserver;
256
+ renderedDimensionsByTrack: Record<string, RenderedDimensionsReport>;
248
257
  statsByView: Record<string, ViewStats>;
249
258
  subscriptions: StatsSubscription[];
250
259
  numFailedStatsReports: number;
@@ -261,6 +270,7 @@ declare const getNumMissingTrackSsrcLookups: () => number;
261
270
  declare const getNumFailedTrackSsrcLookups: () => number;
262
271
  declare const getUpdatedStats: () => Promise<Record<string, ViewStats> | undefined> | undefined;
263
272
  declare const setClientProvider: (provider: () => StatsClient[]) => () => StatsClient[];
273
+ declare const updateRenderedDimensions: (trackId: string, data: RenderedDimensionsReport) => void;
264
274
  declare function subscribeStats(subscription: StatsSubscription, options?: StatsMonitorOptions, state?: StatsMonitorState): {
265
275
  stop(): void;
266
276
  };
@@ -1641,6 +1651,7 @@ declare class P2pRtcManager implements RtcManager {
1641
1651
  analytics: P2PAnalytics;
1642
1652
  _rtcStatsDisconnectTimeout?: ReturnType<typeof setTimeout>;
1643
1653
  _webcamPaused?: boolean;
1654
+ _videoTrackIdByStreamId: Record<string, string>;
1644
1655
  constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features }: RtcManagerOptions);
1645
1656
  numberOfPeerconnections(): number;
1646
1657
  isInitializedWith({ selfId, roomName, isSfu }: {
@@ -1693,7 +1704,10 @@ declare class P2pRtcManager implements RtcManager {
1693
1704
  clientId: string;
1694
1705
  }): Session;
1695
1706
  disconnect(clientId: string): void;
1696
- updateStreamResolution(): void;
1707
+ updateStreamResolution(streamId: string, _ignored: any, { width, height, }: {
1708
+ width: number;
1709
+ height: number;
1710
+ }): void;
1697
1711
  stopOrResumeAudio(): void;
1698
1712
  _handleStopOrResumeVideo({ enable, track }: {
1699
1713
  enable: boolean;
@@ -1745,6 +1759,12 @@ declare const rtcStats: {
1745
1759
  trace: (...args: any) => void;
1746
1760
  close: () => void;
1747
1761
  connect: () => void;
1762
+ } | {
1763
+ connected: boolean;
1764
+ attemptedConnectedAtLeastOnce: boolean;
1765
+ trace: () => void;
1766
+ close: () => void;
1767
+ connect: () => void;
1748
1768
  };
1749
1769
  };
1750
1770
 
@@ -2089,5 +2109,5 @@ declare class VegaRtcManager implements RtcManager {
2089
2109
  hasClient(clientId: string): boolean;
2090
2110
  }
2091
2111
 
2092
- export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, _default as rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, variance };
2112
+ export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, _default as rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, updateRenderedDimensions, variance };
2093
2113
  export type { AddCameraStreamOptions, AddSpotlightRequest, AudioEnableRequest, AudioEnableRequestedEvent, AudioEnabledEvent, BreakoutConfig, BreakoutGroupJoinedEvent, BreakoutSessionUpdatedEvent, BuildDeviceListOptions, CannotJoinUnclaimedRoomError, ChatFileShare, ChatMessage, ChatMessageError, ChatMessageRemoved, ChatMessageRequest, ClearableTimeout, ClientKickedEvent, ClientLeftEvent, ClientMetadataPayload, ClientMetadataReceivedEvent, ClientRole, ClientUnableToJoinEvent, CloudRecordingStartedEvent, Codec, ConnectionStatus, Credentials, FileShareErrorCode, FileUploadUrl, ForbiddenError, ForbiddenErrorNames, GetConstraintsOptions, GetDeviceDataResult, GetMediaConstraintsOptions, GetStreamOptions, GetStreamResult, GetUpdatedDevicesResult, GetUserMediaAttempt, GetUserMediaAttemptOutcome, HostPresenceControlsError, IdentifyDeviceRequest, InternalServerError, InvalidAssistantKeyError, IssuesAndMetricsByView, JoinRoomRequest, KnockAcceptedEvent, KnockRejectedEvent, KnockRoomRequest, KnockerLeftEvent, LiveCaptionEvent, LiveCaptionsStartedEvent, LiveCaptionsStoppedEvent, LiveTranscriptionStartedEvent, LiveTranscriptionStoppedEvent, MaxViewerLimitReachedError, MediaPrefs, Metric, NewClientEvent, OrganizationAssistantNotEnabledError, OrganizationAssistantNotFoundError, OrganizationPlanExhaustedError, RemoveScreenshareStreamOptions, RemoveSpotlightRequest, RoleName, RoomConcurrencyControlsError, RoomEmptyError, RoomFullError, RoomJoinPermissionDeniedError, RoomJoinedErrors, RoomJoinedEvent, RoomJoinedSuccess, RoomKnockedEvent, RoomLockedError, RoomLockedEvent, RoomMeetingTimeExhaustedError, RoomMode, RoomSessionEndedEvent, RtcClientConnectionStatusChangedPayload, RtcEventEmitter, RtcEvents, RtcLocalStreamTrackAddedPayload, RtcLocalStreamTrackRemovedPayload, RtcManager, RtcManagerCreatedPayload, RtcManagerOptions, RtcStreamAddedPayload, ScreenshareStartedEvent, ScreenshareStoppedEvent, SendClientMetadataRequest, SignalClient, SignalEvents, SignalIceCandidateMessage, SignalIceEndOfCandidatesMessage, SignalIceServer, SignalKnocker, SignalMediaServerConfig, SignalRTCSessionDescription, SignalReadyToReceiveOfferMessage, SignalRequests, SignalRoom, SignalSDPMessage, SignalSFUServer, SignalTurnServer, SocketConf, SocketManager, Spotlight, SpotlightAddedEvent, SpotlightRemovedEvent, StatsMonitorOptions, StatsMonitorState, StatsSubscription, TurnTransportProtocol, UniqueRoleAlreadyInRoomError, UpdatedDeviceInfo, UpdatedDevicesInfo, VegaConnectionOptions, VegaRtcManagerOptions, VideoEnableRequest, VideoEnableRequestedEvent, VideoEnabledEvent, WebRTCProvider };
package/dist/index.d.ts CHANGED
@@ -27,10 +27,16 @@ type PressureRecord = {
27
27
  state: string;
28
28
  time: number;
29
29
  };
30
+ type RenderedDimensionsReport = {
31
+ time: number;
32
+ width: number;
33
+ height: number;
34
+ };
30
35
  interface TrackStats {
31
36
  startTime: number;
32
37
  updated: number;
33
38
  ssrcs: Record<string, SsrcStats>;
39
+ renderedDimensions?: RenderedDimensionsReport;
34
40
  }
35
41
  interface ViewStats {
36
42
  startTime?: number;
@@ -93,6 +99,7 @@ interface SsrcStats {
93
99
  encodeTime?: number;
94
100
  sourceWidth?: number;
95
101
  sourceFps?: number;
102
+ freezeRate?: number;
96
103
  }
97
104
 
98
105
  interface IssueDetector {
@@ -106,6 +113,7 @@ interface IssueCheckData {
106
113
  clients: StatsClient[];
107
114
  kind: string;
108
115
  track: MediaStreamTrack | MediaStreamTrackWithDenoiserContext | undefined;
116
+ renderedDimensions?: RenderedDimensionsReport;
109
117
  trackStats?: TrackStats;
110
118
  stats?: ViewStats;
111
119
  hasLiveTrack: boolean;
@@ -236,7 +244,7 @@ interface StatsMonitor {
236
244
  stop: () => void;
237
245
  }
238
246
  interface StatsSubscription {
239
- onUpdatedStats: (statsByView: Record<string, ViewStats>, clients: StatsClient[]) => void;
247
+ onUpdatedStats: (statsByView: Record<string, ViewStats>, clients: StatsClient[], renderedDimensionsByTrack: Record<string, RenderedDimensionsReport>) => void;
240
248
  }
241
249
  interface StatsMonitorState {
242
250
  currentMonitor: StatsMonitor | null;
@@ -245,6 +253,7 @@ interface StatsMonitorState {
245
253
  lastUpdateTime: number;
246
254
  nextTimeout?: number;
247
255
  pressureObserver?: PressureObserver;
256
+ renderedDimensionsByTrack: Record<string, RenderedDimensionsReport>;
248
257
  statsByView: Record<string, ViewStats>;
249
258
  subscriptions: StatsSubscription[];
250
259
  numFailedStatsReports: number;
@@ -261,6 +270,7 @@ declare const getNumMissingTrackSsrcLookups: () => number;
261
270
  declare const getNumFailedTrackSsrcLookups: () => number;
262
271
  declare const getUpdatedStats: () => Promise<Record<string, ViewStats> | undefined> | undefined;
263
272
  declare const setClientProvider: (provider: () => StatsClient[]) => () => StatsClient[];
273
+ declare const updateRenderedDimensions: (trackId: string, data: RenderedDimensionsReport) => void;
264
274
  declare function subscribeStats(subscription: StatsSubscription, options?: StatsMonitorOptions, state?: StatsMonitorState): {
265
275
  stop(): void;
266
276
  };
@@ -1641,6 +1651,7 @@ declare class P2pRtcManager implements RtcManager {
1641
1651
  analytics: P2PAnalytics;
1642
1652
  _rtcStatsDisconnectTimeout?: ReturnType<typeof setTimeout>;
1643
1653
  _webcamPaused?: boolean;
1654
+ _videoTrackIdByStreamId: Record<string, string>;
1644
1655
  constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features }: RtcManagerOptions);
1645
1656
  numberOfPeerconnections(): number;
1646
1657
  isInitializedWith({ selfId, roomName, isSfu }: {
@@ -1693,7 +1704,10 @@ declare class P2pRtcManager implements RtcManager {
1693
1704
  clientId: string;
1694
1705
  }): Session;
1695
1706
  disconnect(clientId: string): void;
1696
- updateStreamResolution(): void;
1707
+ updateStreamResolution(streamId: string, _ignored: any, { width, height, }: {
1708
+ width: number;
1709
+ height: number;
1710
+ }): void;
1697
1711
  stopOrResumeAudio(): void;
1698
1712
  _handleStopOrResumeVideo({ enable, track }: {
1699
1713
  enable: boolean;
@@ -1745,6 +1759,12 @@ declare const rtcStats: {
1745
1759
  trace: (...args: any) => void;
1746
1760
  close: () => void;
1747
1761
  connect: () => void;
1762
+ } | {
1763
+ connected: boolean;
1764
+ attemptedConnectedAtLeastOnce: boolean;
1765
+ trace: () => void;
1766
+ close: () => void;
1767
+ connect: () => void;
1748
1768
  };
1749
1769
  };
1750
1770
 
@@ -2089,5 +2109,5 @@ declare class VegaRtcManager implements RtcManager {
2089
2109
  hasClient(clientId: string): boolean;
2090
2110
  }
2091
2111
 
2092
- export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, _default as rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, variance };
2112
+ export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, _default as rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, updateRenderedDimensions, variance };
2093
2113
  export type { AddCameraStreamOptions, AddSpotlightRequest, AudioEnableRequest, AudioEnableRequestedEvent, AudioEnabledEvent, BreakoutConfig, BreakoutGroupJoinedEvent, BreakoutSessionUpdatedEvent, BuildDeviceListOptions, CannotJoinUnclaimedRoomError, ChatFileShare, ChatMessage, ChatMessageError, ChatMessageRemoved, ChatMessageRequest, ClearableTimeout, ClientKickedEvent, ClientLeftEvent, ClientMetadataPayload, ClientMetadataReceivedEvent, ClientRole, ClientUnableToJoinEvent, CloudRecordingStartedEvent, Codec, ConnectionStatus, Credentials, FileShareErrorCode, FileUploadUrl, ForbiddenError, ForbiddenErrorNames, GetConstraintsOptions, GetDeviceDataResult, GetMediaConstraintsOptions, GetStreamOptions, GetStreamResult, GetUpdatedDevicesResult, GetUserMediaAttempt, GetUserMediaAttemptOutcome, HostPresenceControlsError, IdentifyDeviceRequest, InternalServerError, InvalidAssistantKeyError, IssuesAndMetricsByView, JoinRoomRequest, KnockAcceptedEvent, KnockRejectedEvent, KnockRoomRequest, KnockerLeftEvent, LiveCaptionEvent, LiveCaptionsStartedEvent, LiveCaptionsStoppedEvent, LiveTranscriptionStartedEvent, LiveTranscriptionStoppedEvent, MaxViewerLimitReachedError, MediaPrefs, Metric, NewClientEvent, OrganizationAssistantNotEnabledError, OrganizationAssistantNotFoundError, OrganizationPlanExhaustedError, RemoveScreenshareStreamOptions, RemoveSpotlightRequest, RoleName, RoomConcurrencyControlsError, RoomEmptyError, RoomFullError, RoomJoinPermissionDeniedError, RoomJoinedErrors, RoomJoinedEvent, RoomJoinedSuccess, RoomKnockedEvent, RoomLockedError, RoomLockedEvent, RoomMeetingTimeExhaustedError, RoomMode, RoomSessionEndedEvent, RtcClientConnectionStatusChangedPayload, RtcEventEmitter, RtcEvents, RtcLocalStreamTrackAddedPayload, RtcLocalStreamTrackRemovedPayload, RtcManager, RtcManagerCreatedPayload, RtcManagerOptions, RtcStreamAddedPayload, ScreenshareStartedEvent, ScreenshareStoppedEvent, SendClientMetadataRequest, SignalClient, SignalEvents, SignalIceCandidateMessage, SignalIceEndOfCandidatesMessage, SignalIceServer, SignalKnocker, SignalMediaServerConfig, SignalRTCSessionDescription, SignalReadyToReceiveOfferMessage, SignalRequests, SignalRoom, SignalSDPMessage, SignalSFUServer, SignalTurnServer, SocketConf, SocketManager, Spotlight, SpotlightAddedEvent, SpotlightRemovedEvent, StatsMonitorOptions, StatsMonitorState, StatsSubscription, TurnTransportProtocol, UniqueRoleAlreadyInRoomError, UpdatedDeviceInfo, UpdatedDevicesInfo, VegaConnectionOptions, VegaRtcManagerOptions, VideoEnableRequest, VideoEnableRequestedEvent, VideoEnabledEvent, WebRTCProvider };
package/dist/index.mjs CHANGED
@@ -201,9 +201,22 @@ function rtcStatsConnection(wsURL, logger = console) {
201
201
  return connection;
202
202
  }
203
203
  const RTCSTATS_URL = "wss://rtcstats.srv.whereby.com";
204
- const server = rtcStatsConnection(RTCSTATS_URL);
205
- const stats = rtcstats(server.trace, 10000, [""]);
206
- resetDelta = (stats === null || stats === void 0 ? void 0 : stats.resetDelta) || noop;
204
+ const noopServer = {
205
+ connected: false,
206
+ attemptedConnectedAtLeastOnce: false,
207
+ trace: noop,
208
+ close: noop,
209
+ connect: noop,
210
+ };
211
+ let server;
212
+ if (typeof window !== "undefined") {
213
+ server = rtcStatsConnection(RTCSTATS_URL);
214
+ const stats = rtcstats(server.trace, 10000, [""]);
215
+ resetDelta = (stats === null || stats === void 0 ? void 0 : stats.resetDelta) || noop;
216
+ }
217
+ else {
218
+ server = noopServer;
219
+ }
207
220
  const rtcStats = {
208
221
  sendEvent: (type, value) => {
209
222
  server.trace("customEvent", null, {
@@ -220,7 +233,7 @@ const rtcStats = {
220
233
  server,
221
234
  };
222
235
 
223
- const debugOn = new URLSearchParams(window.location.search).has("debug");
236
+ const debugOn = (typeof window !== "undefined" && new URLSearchParams(window.location.search).has("debug"));
224
237
  class Logger {
225
238
  constructor() {
226
239
  this._isEnabled = false;
@@ -1422,6 +1435,8 @@ function captureVideoSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, t
1422
1435
  const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
1423
1436
  ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
1424
1437
  ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
1438
+ const freezeCountDiff = (currentSsrcStats.freezeCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.freezeCount) || 0);
1439
+ ssrcMetrics.freezeRate = (freezeCountDiff * 1000) / timeDiff;
1425
1440
  }
1426
1441
  else {
1427
1442
  const kfCountDiff = currentSsrcStats.keyFramesEncoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.keyFramesEncoded) || 0);
@@ -1451,7 +1466,7 @@ const peerConnectionData = new WeakMap();
1451
1466
  const removePeerConnection = (pc) => {
1452
1467
  peerConnections = peerConnections.filter((old) => old !== pc);
1453
1468
  };
1454
- if (window.RTCPeerConnection) {
1469
+ if (typeof window !== "undefined" && window.RTCPeerConnection) {
1455
1470
  const OriginalRTCPeerConnection = window.RTCPeerConnection;
1456
1471
  function PatchedRTCPeerConnection(rtcConfig) {
1457
1472
  const pc = new OriginalRTCPeerConnection(rtcConfig);
@@ -1567,15 +1582,17 @@ const getOrCreateSsrcMetricsContainer = (statsByView, time, pcIndex, clientId, t
1567
1582
  ssrcStats.updated = time;
1568
1583
  return ssrcStats;
1569
1584
  };
1570
- const removeNonUpdatedStats = (statsByView, time) => {
1585
+ const removeNonUpdatedStats = (statsByView, time, renderedDimensionsByTrack) => {
1571
1586
  Object.entries(statsByView).forEach(([viewId, viewStats]) => {
1572
1587
  if (viewStats.updated !== undefined && viewStats.updated < time) {
1588
+ Object.keys(viewStats.tracks).forEach((trackId) => delete renderedDimensionsByTrack[trackId]);
1573
1589
  delete statsByView[viewId];
1574
1590
  }
1575
1591
  else {
1576
1592
  Object.entries(viewStats.tracks).forEach(([trackId, trackStats]) => {
1577
1593
  if (trackStats.updated < time) {
1578
1594
  delete viewStats.tracks[trackId];
1595
+ delete renderedDimensionsByTrack[trackId];
1579
1596
  }
1580
1597
  else {
1581
1598
  Object.entries(trackStats.ssrcs).forEach(([ssrc, ssrcStats]) => {
@@ -1613,7 +1630,7 @@ function collectStats(state_1, _a, immediate_1) {
1613
1630
  if (timeSinceLastUpdate < 400) {
1614
1631
  if (immediate)
1615
1632
  return state.statsByView;
1616
- state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
1633
+ state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients, state.renderedDimensionsByTrack); });
1617
1634
  state.nextTimeout = setTimeout(collectStatsBound, interval);
1618
1635
  return;
1619
1636
  }
@@ -1706,7 +1723,7 @@ function collectStats(state_1, _a, immediate_1) {
1706
1723
  }
1707
1724
  });
1708
1725
  });
1709
- removeNonUpdatedStats(state.statsByView, state.lastUpdateTime);
1726
+ removeNonUpdatedStats(state.statsByView, state.lastUpdateTime, state.renderedDimensionsByTrack);
1710
1727
  Object.entries((defaultViewStats === null || defaultViewStats === void 0 ? void 0 : defaultViewStats.candidatePairs) || {}).forEach(([cpKey, cp]) => {
1711
1728
  const active = cp.lastRtcStatsTime === state.lastUpdateTime;
1712
1729
  cp.active = active;
@@ -1725,7 +1742,7 @@ function collectStats(state_1, _a, immediate_1) {
1725
1742
  return state.statsByView;
1726
1743
  }
1727
1744
  else {
1728
- state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
1745
+ state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients, state.renderedDimensionsByTrack); });
1729
1746
  }
1730
1747
  }
1731
1748
  catch (e) {
@@ -1764,6 +1781,7 @@ const STATE = {
1764
1781
  currentMonitor: null,
1765
1782
  getClients: () => [],
1766
1783
  lastUpdateTime: 0,
1784
+ renderedDimensionsByTrack: {},
1767
1785
  statsByView: {},
1768
1786
  subscriptions: [],
1769
1787
  numFailedStatsReports: 0,
@@ -1782,6 +1800,9 @@ const getNumMissingTrackSsrcLookups = () => numMissingTrackSsrcLookups;
1782
1800
  const getNumFailedTrackSsrcLookups = () => numFailedTrackSsrcLookups;
1783
1801
  const getUpdatedStats = () => { var _a; return (_a = STATE.currentMonitor) === null || _a === void 0 ? void 0 : _a.getUpdatedStats(); };
1784
1802
  const setClientProvider = (provider) => (STATE.getClients = provider);
1803
+ const updateRenderedDimensions = (trackId, data) => {
1804
+ STATE.renderedDimensionsByTrack[trackId] = data;
1805
+ };
1785
1806
  function startStatsMonitor(state, { interval, logger }) {
1786
1807
  const collectStatsBound = collectStats.bind(null, state, { interval, logger });
1787
1808
  let cpuObserver;
@@ -2403,7 +2424,7 @@ function fromLocation({ host = "whereby.com", protocol = "https:" } = {}) {
2403
2424
  subdomain,
2404
2425
  };
2405
2426
  }
2406
- fromLocation(window && window.location);
2427
+ fromLocation(typeof window !== "undefined" ? window.location : undefined);
2407
2428
 
2408
2429
  var _a$2, _b$1;
2409
2430
  const adapter$2 = (_a$2 = adapterRaw.default) !== null && _a$2 !== void 0 ? _a$2 : adapterRaw;
@@ -2502,6 +2523,7 @@ class P2pRtcManager {
2502
2523
  this._features = features || {};
2503
2524
  this._isAudioOnlyMode = false;
2504
2525
  this._closed = false;
2526
+ this._videoTrackIdByStreamId = {};
2505
2527
  this._fetchMediaServersTimer = null;
2506
2528
  this._stopCameraTimeout = null;
2507
2529
  this._icePublicIPGatheringTimeoutID = null;
@@ -2982,6 +3004,8 @@ class P2pRtcManager {
2982
3004
  });
2983
3005
  return;
2984
3006
  }
3007
+ if (event.track.kind === "video")
3008
+ this._videoTrackIdByStreamId[stream.id] = event.track.id;
2985
3009
  if (session.streamIds.indexOf(stream.id) === -1) {
2986
3010
  session.streamIds.push(stream.id);
2987
3011
  this._emit(EVENTS.STREAM_ADDED, {
@@ -3412,7 +3436,11 @@ class P2pRtcManager {
3412
3436
  }, 60 * 1000);
3413
3437
  }
3414
3438
  }
3415
- updateStreamResolution() { }
3439
+ updateStreamResolution(streamId, _ignored, { width, height, }) {
3440
+ const trackId = this._videoTrackIdByStreamId[streamId];
3441
+ if (trackId)
3442
+ updateRenderedDimensions(trackId, { width, height, time: Date.now() });
3443
+ }
3416
3444
  stopOrResumeAudio() {
3417
3445
  }
3418
3446
  _handleStopOrResumeVideo({ enable, track }) {
@@ -5553,7 +5581,7 @@ class VegaRtcManager {
5553
5581
  this._syncIncomingStreamsWithPWA(clientId);
5554
5582
  }
5555
5583
  updateStreamResolution(streamId, _ignored, { width, height, }) {
5556
- var _a, _b;
5584
+ var _a, _b, _c;
5557
5585
  logger$2.info("updateStreamResolution()", { streamId, width, height });
5558
5586
  const consumerId = this._streamIdToVideoConsumerId.get(streamId);
5559
5587
  const consumer = this._consumers.get(consumerId);
@@ -5561,17 +5589,18 @@ class VegaRtcManager {
5561
5589
  this._streamIdToVideoResolution.set(streamId, { width, height });
5562
5590
  return;
5563
5591
  }
5592
+ updateRenderedDimensions((_a = consumer.track) === null || _a === void 0 ? void 0 : _a.id, { width, height, time: Date.now() });
5564
5593
  const numberOfActiveVideos = getNumberOfActiveVideos(this._consumers);
5565
5594
  const numberOfTemporalLayers = getNumberOfTemporalLayers(consumer);
5566
5595
  const { spatialLayer, temporalLayer } = getLayers({ width, height }, {
5567
5596
  numberOfActiveVideos,
5568
5597
  numberOfTemporalLayers,
5569
- uncappedSingleRemoteVideoOn: (_a = this._features) === null || _a === void 0 ? void 0 : _a.uncappedSingleRemoteVideoOn,
5598
+ uncappedSingleRemoteVideoOn: (_b = this._features) === null || _b === void 0 ? void 0 : _b.uncappedSingleRemoteVideoOn,
5570
5599
  });
5571
5600
  if (consumer.appData.spatialLayer !== spatialLayer || consumer.appData.temporalLayer !== temporalLayer) {
5572
5601
  consumer.appData.spatialLayer = spatialLayer;
5573
5602
  consumer.appData.temporalLayer = temporalLayer;
5574
- (_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.message("setConsumersPreferredLayers", {
5603
+ (_c = this._vegaConnection) === null || _c === void 0 ? void 0 : _c.message("setConsumersPreferredLayers", {
5575
5604
  consumerIds: [consumerId],
5576
5605
  spatialLayer,
5577
5606
  temporalLayer,
@@ -5986,6 +6015,92 @@ const getRoomMode = () => {
5986
6015
  return roomMode;
5987
6016
  };
5988
6017
 
6018
+ const createQualityDetector = (type) => {
6019
+ return {
6020
+ id: `quality-${type}`,
6021
+ enabled: ({ client }) => !client.isLocalClient && !client.isPresentation,
6022
+ check: ({ ssrc0, renderedDimensions, hasLiveTrack, kind }) => {
6023
+ if (!hasLiveTrack)
6024
+ return true;
6025
+ if (!ssrc0)
6026
+ return true;
6027
+ if (!ssrc0.bitrate)
6028
+ return true;
6029
+ if (kind === "video") {
6030
+ if (renderedDimensions && renderedDimensions.width && renderedDimensions.height) {
6031
+ const maxSideRendered = Math.max(renderedDimensions.width, renderedDimensions.height);
6032
+ const maxSideReceived = Math.max(ssrc0.width || 0, ssrc0.height || 0);
6033
+ const fpsReceived = ssrc0.fps || 0;
6034
+ const hadFreeze = !!ssrc0.freezeRate;
6035
+ const bitrate = ssrc0.bitrate || 0;
6036
+ if (maxSideRendered < 100) {
6037
+ if (type === "warning" && maxSideReceived < 160)
6038
+ return true;
6039
+ if (type === "critical" && maxSideReceived < 90)
6040
+ return true;
6041
+ if (type === "warning" && fpsReceived < 5)
6042
+ return true;
6043
+ if (type === "critical" && fpsReceived < 2)
6044
+ return true;
6045
+ if (type === "warning" && bitrate < 10000)
6046
+ return true;
6047
+ if (type === "critical" && bitrate < 5000)
6048
+ return true;
6049
+ }
6050
+ else if (maxSideRendered < 480) {
6051
+ if (type === "warning" && maxSideReceived < 240)
6052
+ return true;
6053
+ if (type === "critical" && maxSideReceived < 120)
6054
+ return true;
6055
+ if (type === "warning" && fpsReceived < 14)
6056
+ return true;
6057
+ if (type === "critical" && fpsReceived < 9)
6058
+ return true;
6059
+ if (hadFreeze)
6060
+ return true;
6061
+ if (type === "warning" && bitrate < 50000)
6062
+ return true;
6063
+ if (type === "critical" && bitrate < 20000)
6064
+ return true;
6065
+ }
6066
+ else {
6067
+ if (type === "warning" && maxSideReceived < 480)
6068
+ return true;
6069
+ if (type === "critical" && maxSideReceived < 240)
6070
+ return true;
6071
+ if (type === "warning" && fpsReceived < 14)
6072
+ return true;
6073
+ if (type === "critical" && fpsReceived < 9)
6074
+ return true;
6075
+ if (type === "warning" && bitrate < 200000)
6076
+ return true;
6077
+ if (type === "critical" && bitrate < 50000)
6078
+ return true;
6079
+ if (hadFreeze)
6080
+ return true;
6081
+ }
6082
+ }
6083
+ }
6084
+ else if (kind === "audio") {
6085
+ const audioLevel = ssrc0.audioLevel || 0;
6086
+ const concealment = ssrc0.audioConcealment || 0;
6087
+ const acceleration = ssrc0.audioAcceleration || 0;
6088
+ const deceleration = ssrc0.audioDeceleration || 0;
6089
+ const audioDistortion = concealment + acceleration + deceleration;
6090
+ if (audioLevel >= 0.01) {
6091
+ if (type === "warning" && audioDistortion > 0.1)
6092
+ return true;
6093
+ if (type === "critical" && audioDistortion > 0.2)
6094
+ return true;
6095
+ }
6096
+ }
6097
+ return false;
6098
+ },
6099
+ };
6100
+ };
6101
+ const qualityWarningDetector = createQualityDetector("warning");
6102
+ const qualityCriticalDetector = createQualityDetector("critical");
6103
+
5989
6104
  const badNetworkIssueDetector = {
5990
6105
  id: "bad-network",
5991
6106
  enabled: ({ hasLiveTrack, ssrcs }) => hasLiveTrack && ssrcs.length > 0,
@@ -6209,6 +6324,8 @@ const issueDetectors = [
6209
6324
  (ssrc0.audioLevel || 0) >= 0.001 &&
6210
6325
  (ssrc0.audioAcceleration || 0) >= 0.1,
6211
6326
  },
6327
+ qualityWarningDetector,
6328
+ qualityCriticalDetector,
6212
6329
  ];
6213
6330
 
6214
6331
  let subscriptions = [];
@@ -6363,7 +6480,7 @@ function issueDetectorOrMetricEnabled(issueDetectorOrMetric, checkData) {
6363
6480
  }
6364
6481
  return enabled && (issueDetectorOrMetric.enabled ? issueDetectorOrMetric.enabled(checkData) : true);
6365
6482
  }
6366
- function onUpdatedStats(statsByView, clients) {
6483
+ function onUpdatedStats(statsByView, clients, renderedDimensionsByTrack) {
6367
6484
  Object.values(aggregatedMetrics).forEach((metricData) => {
6368
6485
  metricData.curTicks = 0;
6369
6486
  metricData.curSum = 0;
@@ -6401,6 +6518,7 @@ function onUpdatedStats(statsByView, clients) {
6401
6518
  kind,
6402
6519
  track,
6403
6520
  trackStats,
6521
+ renderedDimensions: (track === null || track === void 0 ? void 0 : track.id) ? renderedDimensionsByTrack[track.id] : undefined,
6404
6522
  stats,
6405
6523
  hasLiveTrack,
6406
6524
  ssrc0,
@@ -7043,7 +7161,7 @@ class BandwidthTester extends EventEmitter {
7043
7161
  }
7044
7162
 
7045
7163
  const logger = new Logger();
7046
- const isMobile = /mobi/i.test(navigator.userAgent);
7164
+ const isMobile = typeof navigator !== "undefined" && /mobi/i.test(navigator.userAgent);
7047
7165
  class NoDevicesError extends Error {
7048
7166
  constructor(...args) {
7049
7167
  super(...args);
@@ -7301,8 +7419,8 @@ function hasGetDisplayMedia() {
7301
7419
  }
7302
7420
  const defaultDisplayMediaConstraints = {
7303
7421
  video: {
7304
- width: { max: window.screen.width },
7305
- height: { max: window.screen.height },
7422
+ width: { max: typeof window !== "undefined" ? window.screen.width : 0 },
7423
+ height: { max: typeof window !== "undefined" ? window.screen.height : 0 },
7306
7424
  },
7307
7425
  };
7308
7426
  function getDisplayMedia(constraints = defaultDisplayMediaConstraints, contentHint = "detail") {
@@ -7393,4 +7511,4 @@ var RtcEventNames;
7393
7511
  RtcEventNames["stream_added"] = "stream_added";
7394
7512
  })(RtcEventNames || (RtcEventNames = {}));
7395
7513
 
7396
- export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, variance };
7514
+ export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, updateRenderedDimensions, variance };
@@ -201,9 +201,22 @@ function rtcStatsConnection(wsURL, logger = console) {
201
201
  return connection;
202
202
  }
203
203
  const RTCSTATS_URL = "wss://rtcstats.srv.whereby.com";
204
- const server = rtcStatsConnection(RTCSTATS_URL);
205
- const stats = rtcstats(server.trace, 10000, [""]);
206
- resetDelta = (stats === null || stats === void 0 ? void 0 : stats.resetDelta) || noop;
204
+ const noopServer = {
205
+ connected: false,
206
+ attemptedConnectedAtLeastOnce: false,
207
+ trace: noop,
208
+ close: noop,
209
+ connect: noop,
210
+ };
211
+ let server;
212
+ if (typeof window !== "undefined") {
213
+ server = rtcStatsConnection(RTCSTATS_URL);
214
+ const stats = rtcstats(server.trace, 10000, [""]);
215
+ resetDelta = (stats === null || stats === void 0 ? void 0 : stats.resetDelta) || noop;
216
+ }
217
+ else {
218
+ server = noopServer;
219
+ }
207
220
  const rtcStats = {
208
221
  sendEvent: (type, value) => {
209
222
  server.trace("customEvent", null, {
@@ -220,7 +233,7 @@ const rtcStats = {
220
233
  server,
221
234
  };
222
235
 
223
- const debugOn = new URLSearchParams(window.location.search).has("debug");
236
+ const debugOn = (typeof window !== "undefined" && new URLSearchParams(window.location.search).has("debug"));
224
237
  class Logger {
225
238
  constructor() {
226
239
  this._isEnabled = false;
@@ -1422,6 +1435,8 @@ function captureVideoSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, t
1422
1435
  const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
1423
1436
  ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
1424
1437
  ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
1438
+ const freezeCountDiff = (currentSsrcStats.freezeCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.freezeCount) || 0);
1439
+ ssrcMetrics.freezeRate = (freezeCountDiff * 1000) / timeDiff;
1425
1440
  }
1426
1441
  else {
1427
1442
  const kfCountDiff = currentSsrcStats.keyFramesEncoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.keyFramesEncoded) || 0);
@@ -1451,7 +1466,7 @@ const peerConnectionData = new WeakMap();
1451
1466
  const removePeerConnection = (pc) => {
1452
1467
  peerConnections = peerConnections.filter((old) => old !== pc);
1453
1468
  };
1454
- if (window.RTCPeerConnection) {
1469
+ if (typeof window !== "undefined" && window.RTCPeerConnection) {
1455
1470
  const OriginalRTCPeerConnection = window.RTCPeerConnection;
1456
1471
  function PatchedRTCPeerConnection(rtcConfig) {
1457
1472
  const pc = new OriginalRTCPeerConnection(rtcConfig);
@@ -1567,15 +1582,17 @@ const getOrCreateSsrcMetricsContainer = (statsByView, time, pcIndex, clientId, t
1567
1582
  ssrcStats.updated = time;
1568
1583
  return ssrcStats;
1569
1584
  };
1570
- const removeNonUpdatedStats = (statsByView, time) => {
1585
+ const removeNonUpdatedStats = (statsByView, time, renderedDimensionsByTrack) => {
1571
1586
  Object.entries(statsByView).forEach(([viewId, viewStats]) => {
1572
1587
  if (viewStats.updated !== undefined && viewStats.updated < time) {
1588
+ Object.keys(viewStats.tracks).forEach((trackId) => delete renderedDimensionsByTrack[trackId]);
1573
1589
  delete statsByView[viewId];
1574
1590
  }
1575
1591
  else {
1576
1592
  Object.entries(viewStats.tracks).forEach(([trackId, trackStats]) => {
1577
1593
  if (trackStats.updated < time) {
1578
1594
  delete viewStats.tracks[trackId];
1595
+ delete renderedDimensionsByTrack[trackId];
1579
1596
  }
1580
1597
  else {
1581
1598
  Object.entries(trackStats.ssrcs).forEach(([ssrc, ssrcStats]) => {
@@ -1613,7 +1630,7 @@ function collectStats(state_1, _a, immediate_1) {
1613
1630
  if (timeSinceLastUpdate < 400) {
1614
1631
  if (immediate)
1615
1632
  return state.statsByView;
1616
- state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
1633
+ state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients, state.renderedDimensionsByTrack); });
1617
1634
  state.nextTimeout = setTimeout(collectStatsBound, interval);
1618
1635
  return;
1619
1636
  }
@@ -1706,7 +1723,7 @@ function collectStats(state_1, _a, immediate_1) {
1706
1723
  }
1707
1724
  });
1708
1725
  });
1709
- removeNonUpdatedStats(state.statsByView, state.lastUpdateTime);
1726
+ removeNonUpdatedStats(state.statsByView, state.lastUpdateTime, state.renderedDimensionsByTrack);
1710
1727
  Object.entries((defaultViewStats === null || defaultViewStats === void 0 ? void 0 : defaultViewStats.candidatePairs) || {}).forEach(([cpKey, cp]) => {
1711
1728
  const active = cp.lastRtcStatsTime === state.lastUpdateTime;
1712
1729
  cp.active = active;
@@ -1725,7 +1742,7 @@ function collectStats(state_1, _a, immediate_1) {
1725
1742
  return state.statsByView;
1726
1743
  }
1727
1744
  else {
1728
- state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
1745
+ state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients, state.renderedDimensionsByTrack); });
1729
1746
  }
1730
1747
  }
1731
1748
  catch (e) {
@@ -1764,6 +1781,7 @@ const STATE = {
1764
1781
  currentMonitor: null,
1765
1782
  getClients: () => [],
1766
1783
  lastUpdateTime: 0,
1784
+ renderedDimensionsByTrack: {},
1767
1785
  statsByView: {},
1768
1786
  subscriptions: [],
1769
1787
  numFailedStatsReports: 0,
@@ -1782,6 +1800,9 @@ const getNumMissingTrackSsrcLookups = () => numMissingTrackSsrcLookups;
1782
1800
  const getNumFailedTrackSsrcLookups = () => numFailedTrackSsrcLookups;
1783
1801
  const getUpdatedStats = () => { var _a; return (_a = STATE.currentMonitor) === null || _a === void 0 ? void 0 : _a.getUpdatedStats(); };
1784
1802
  const setClientProvider = (provider) => (STATE.getClients = provider);
1803
+ const updateRenderedDimensions = (trackId, data) => {
1804
+ STATE.renderedDimensionsByTrack[trackId] = data;
1805
+ };
1785
1806
  function startStatsMonitor(state, { interval, logger }) {
1786
1807
  const collectStatsBound = collectStats.bind(null, state, { interval, logger });
1787
1808
  let cpuObserver;
@@ -2403,7 +2424,7 @@ function fromLocation({ host = "whereby.com", protocol = "https:" } = {}) {
2403
2424
  subdomain,
2404
2425
  };
2405
2426
  }
2406
- fromLocation(window && window.location);
2427
+ fromLocation(typeof window !== "undefined" ? window.location : undefined);
2407
2428
 
2408
2429
  var _a$2, _b$1;
2409
2430
  const adapter$2 = (_a$2 = adapterRaw.default) !== null && _a$2 !== void 0 ? _a$2 : adapterRaw;
@@ -2502,6 +2523,7 @@ class P2pRtcManager {
2502
2523
  this._features = features || {};
2503
2524
  this._isAudioOnlyMode = false;
2504
2525
  this._closed = false;
2526
+ this._videoTrackIdByStreamId = {};
2505
2527
  this._fetchMediaServersTimer = null;
2506
2528
  this._stopCameraTimeout = null;
2507
2529
  this._icePublicIPGatheringTimeoutID = null;
@@ -2982,6 +3004,8 @@ class P2pRtcManager {
2982
3004
  });
2983
3005
  return;
2984
3006
  }
3007
+ if (event.track.kind === "video")
3008
+ this._videoTrackIdByStreamId[stream.id] = event.track.id;
2985
3009
  if (session.streamIds.indexOf(stream.id) === -1) {
2986
3010
  session.streamIds.push(stream.id);
2987
3011
  this._emit(EVENTS.STREAM_ADDED, {
@@ -3412,7 +3436,11 @@ class P2pRtcManager {
3412
3436
  }, 60 * 1000);
3413
3437
  }
3414
3438
  }
3415
- updateStreamResolution() { }
3439
+ updateStreamResolution(streamId, _ignored, { width, height, }) {
3440
+ const trackId = this._videoTrackIdByStreamId[streamId];
3441
+ if (trackId)
3442
+ updateRenderedDimensions(trackId, { width, height, time: Date.now() });
3443
+ }
3416
3444
  stopOrResumeAudio() {
3417
3445
  }
3418
3446
  _handleStopOrResumeVideo({ enable, track }) {
@@ -5553,7 +5581,7 @@ class VegaRtcManager {
5553
5581
  this._syncIncomingStreamsWithPWA(clientId);
5554
5582
  }
5555
5583
  updateStreamResolution(streamId, _ignored, { width, height, }) {
5556
- var _a, _b;
5584
+ var _a, _b, _c;
5557
5585
  logger$2.info("updateStreamResolution()", { streamId, width, height });
5558
5586
  const consumerId = this._streamIdToVideoConsumerId.get(streamId);
5559
5587
  const consumer = this._consumers.get(consumerId);
@@ -5561,17 +5589,18 @@ class VegaRtcManager {
5561
5589
  this._streamIdToVideoResolution.set(streamId, { width, height });
5562
5590
  return;
5563
5591
  }
5592
+ updateRenderedDimensions((_a = consumer.track) === null || _a === void 0 ? void 0 : _a.id, { width, height, time: Date.now() });
5564
5593
  const numberOfActiveVideos = getNumberOfActiveVideos(this._consumers);
5565
5594
  const numberOfTemporalLayers = getNumberOfTemporalLayers(consumer);
5566
5595
  const { spatialLayer, temporalLayer } = getLayers({ width, height }, {
5567
5596
  numberOfActiveVideos,
5568
5597
  numberOfTemporalLayers,
5569
- uncappedSingleRemoteVideoOn: (_a = this._features) === null || _a === void 0 ? void 0 : _a.uncappedSingleRemoteVideoOn,
5598
+ uncappedSingleRemoteVideoOn: (_b = this._features) === null || _b === void 0 ? void 0 : _b.uncappedSingleRemoteVideoOn,
5570
5599
  });
5571
5600
  if (consumer.appData.spatialLayer !== spatialLayer || consumer.appData.temporalLayer !== temporalLayer) {
5572
5601
  consumer.appData.spatialLayer = spatialLayer;
5573
5602
  consumer.appData.temporalLayer = temporalLayer;
5574
- (_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.message("setConsumersPreferredLayers", {
5603
+ (_c = this._vegaConnection) === null || _c === void 0 ? void 0 : _c.message("setConsumersPreferredLayers", {
5575
5604
  consumerIds: [consumerId],
5576
5605
  spatialLayer,
5577
5606
  temporalLayer,
@@ -5986,6 +6015,92 @@ const getRoomMode = () => {
5986
6015
  return roomMode;
5987
6016
  };
5988
6017
 
6018
+ const createQualityDetector = (type) => {
6019
+ return {
6020
+ id: `quality-${type}`,
6021
+ enabled: ({ client }) => !client.isLocalClient && !client.isPresentation,
6022
+ check: ({ ssrc0, renderedDimensions, hasLiveTrack, kind }) => {
6023
+ if (!hasLiveTrack)
6024
+ return true;
6025
+ if (!ssrc0)
6026
+ return true;
6027
+ if (!ssrc0.bitrate)
6028
+ return true;
6029
+ if (kind === "video") {
6030
+ if (renderedDimensions && renderedDimensions.width && renderedDimensions.height) {
6031
+ const maxSideRendered = Math.max(renderedDimensions.width, renderedDimensions.height);
6032
+ const maxSideReceived = Math.max(ssrc0.width || 0, ssrc0.height || 0);
6033
+ const fpsReceived = ssrc0.fps || 0;
6034
+ const hadFreeze = !!ssrc0.freezeRate;
6035
+ const bitrate = ssrc0.bitrate || 0;
6036
+ if (maxSideRendered < 100) {
6037
+ if (type === "warning" && maxSideReceived < 160)
6038
+ return true;
6039
+ if (type === "critical" && maxSideReceived < 90)
6040
+ return true;
6041
+ if (type === "warning" && fpsReceived < 5)
6042
+ return true;
6043
+ if (type === "critical" && fpsReceived < 2)
6044
+ return true;
6045
+ if (type === "warning" && bitrate < 10000)
6046
+ return true;
6047
+ if (type === "critical" && bitrate < 5000)
6048
+ return true;
6049
+ }
6050
+ else if (maxSideRendered < 480) {
6051
+ if (type === "warning" && maxSideReceived < 240)
6052
+ return true;
6053
+ if (type === "critical" && maxSideReceived < 120)
6054
+ return true;
6055
+ if (type === "warning" && fpsReceived < 14)
6056
+ return true;
6057
+ if (type === "critical" && fpsReceived < 9)
6058
+ return true;
6059
+ if (hadFreeze)
6060
+ return true;
6061
+ if (type === "warning" && bitrate < 50000)
6062
+ return true;
6063
+ if (type === "critical" && bitrate < 20000)
6064
+ return true;
6065
+ }
6066
+ else {
6067
+ if (type === "warning" && maxSideReceived < 480)
6068
+ return true;
6069
+ if (type === "critical" && maxSideReceived < 240)
6070
+ return true;
6071
+ if (type === "warning" && fpsReceived < 14)
6072
+ return true;
6073
+ if (type === "critical" && fpsReceived < 9)
6074
+ return true;
6075
+ if (type === "warning" && bitrate < 200000)
6076
+ return true;
6077
+ if (type === "critical" && bitrate < 50000)
6078
+ return true;
6079
+ if (hadFreeze)
6080
+ return true;
6081
+ }
6082
+ }
6083
+ }
6084
+ else if (kind === "audio") {
6085
+ const audioLevel = ssrc0.audioLevel || 0;
6086
+ const concealment = ssrc0.audioConcealment || 0;
6087
+ const acceleration = ssrc0.audioAcceleration || 0;
6088
+ const deceleration = ssrc0.audioDeceleration || 0;
6089
+ const audioDistortion = concealment + acceleration + deceleration;
6090
+ if (audioLevel >= 0.01) {
6091
+ if (type === "warning" && audioDistortion > 0.1)
6092
+ return true;
6093
+ if (type === "critical" && audioDistortion > 0.2)
6094
+ return true;
6095
+ }
6096
+ }
6097
+ return false;
6098
+ },
6099
+ };
6100
+ };
6101
+ const qualityWarningDetector = createQualityDetector("warning");
6102
+ const qualityCriticalDetector = createQualityDetector("critical");
6103
+
5989
6104
  const badNetworkIssueDetector = {
5990
6105
  id: "bad-network",
5991
6106
  enabled: ({ hasLiveTrack, ssrcs }) => hasLiveTrack && ssrcs.length > 0,
@@ -6209,6 +6324,8 @@ const issueDetectors = [
6209
6324
  (ssrc0.audioLevel || 0) >= 0.001 &&
6210
6325
  (ssrc0.audioAcceleration || 0) >= 0.1,
6211
6326
  },
6327
+ qualityWarningDetector,
6328
+ qualityCriticalDetector,
6212
6329
  ];
6213
6330
 
6214
6331
  let subscriptions = [];
@@ -6363,7 +6480,7 @@ function issueDetectorOrMetricEnabled(issueDetectorOrMetric, checkData) {
6363
6480
  }
6364
6481
  return enabled && (issueDetectorOrMetric.enabled ? issueDetectorOrMetric.enabled(checkData) : true);
6365
6482
  }
6366
- function onUpdatedStats(statsByView, clients) {
6483
+ function onUpdatedStats(statsByView, clients, renderedDimensionsByTrack) {
6367
6484
  Object.values(aggregatedMetrics).forEach((metricData) => {
6368
6485
  metricData.curTicks = 0;
6369
6486
  metricData.curSum = 0;
@@ -6401,6 +6518,7 @@ function onUpdatedStats(statsByView, clients) {
6401
6518
  kind,
6402
6519
  track,
6403
6520
  trackStats,
6521
+ renderedDimensions: (track === null || track === void 0 ? void 0 : track.id) ? renderedDimensionsByTrack[track.id] : undefined,
6404
6522
  stats,
6405
6523
  hasLiveTrack,
6406
6524
  ssrc0,
@@ -7043,7 +7161,7 @@ class BandwidthTester extends EventEmitter {
7043
7161
  }
7044
7162
 
7045
7163
  const logger = new Logger();
7046
- const isMobile = /mobi/i.test(navigator.userAgent);
7164
+ const isMobile = typeof navigator !== "undefined" && /mobi/i.test(navigator.userAgent);
7047
7165
  class NoDevicesError extends Error {
7048
7166
  constructor(...args) {
7049
7167
  super(...args);
@@ -7301,8 +7419,8 @@ function hasGetDisplayMedia() {
7301
7419
  }
7302
7420
  const defaultDisplayMediaConstraints = {
7303
7421
  video: {
7304
- width: { max: window.screen.width },
7305
- height: { max: window.screen.height },
7422
+ width: { max: typeof window !== "undefined" ? window.screen.width : 0 },
7423
+ height: { max: typeof window !== "undefined" ? window.screen.height : 0 },
7306
7424
  },
7307
7425
  };
7308
7426
  function getDisplayMedia(constraints = defaultDisplayMediaConstraints, contentHint = "detail") {
@@ -7393,5 +7511,5 @@ var RtcEventNames;
7393
7511
  RtcEventNames["stream_added"] = "stream_added";
7394
7512
  })(RtcEventNames || (RtcEventNames = {}));
7395
7513
 
7396
- export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, variance };
7514
+ export { ADDITIONAL_SCREEN_SHARE_SETTINGS, AUDIO_SETTINGS, BandwidthTester, CAMERA_STREAM_ID, EVENTS, FILE_SHARE_ERROR_CODES, KNOCK_MESSAGES, KalmanFilter, Logger, MEDIA_JITTER_BUFFER_TARGET, NoDevicesError, P2pRtcManager, PROTOCOL_ERRORS, PROTOCOL_EVENTS, PROTOCOL_REQUESTS, PROTOCOL_RESPONSES, RELAY_MESSAGES, ReconnectManager, RtcEventNames, RtcManagerDispatcher, SCREEN_SHARE_SETTINGS, SCREEN_SHARE_SIMULCAST_SETTINGS, STREAM_TYPES, ServerSocket, Session, SfuV2Parser, TYPES, VIDEO_SETTINGS_HD, VIDEO_SETTINGS_SD, VIDEO_SETTINGS_VP9, VIDEO_SETTINGS_VP9_LOW_BANDWIDTH, VegaConnection, VegaMediaQualityMonitor, VegaRtcManager, addAbsCaptureTimeExtMap, addExtMap, assert, buildDeviceList, calculateStd, captureAudioSsrcMetrics, captureCandidatePairInfoMetrics, captureCommonSsrcMetrics, captureSsrcInfo, captureVideoSsrcMetrics, cleanSdp, compareLocalDevices, createACFCalculator, createMicAnalyser, createWorker, deprioritizeH264, detectMicrophoneNotWorking, enumerate, external_stun_servers, filterMidExtension, filterMsidSemantic, fromLocation, generateByteString, getConstraints, getCurrentPeerConnections, getDeviceData, getDisplayMedia, getIssuesAndMetrics, getMediaConstraints, getMediaSettings, getMediasoupDeviceAsync, getNumFailedStatsReports, getNumFailedTrackSsrcLookups, getNumMissingTrackSsrcLookups, getPeerConnectionIndex, getStats, getStream, getUpdatedDevices, getUpdatedStats, getUserMedia, hasGetDisplayMedia, ipRegex, isFileShareError, isMobile, issueDetectorOrMetricEnabled, maybeTurnOnly, modifyMediaCapabilities, removePeerConnection, replaceTracksInStream, rtcManagerEvents, rtcStats, setClientProvider, setCodecPreferenceSDP, setPeerConnectionsForTests, setVideoBandwidthUsingSetParameters, sortCodecs, standardDeviation, startPerformanceMonitor, stopStreamTracks, subscribeIssues, subscribeStats, trackAnnotations, turnServerOverride, updateRenderedDimensions, variance };
7397
7515
  //# sourceMappingURL=legacy-esm.js.map
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": "9.2.7",
4
+ "version": "9.3.1",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/whereby/sdk",
7
7
  "repository": {