@whereby.com/media 9.2.7 → 9.3.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
@@ -1442,6 +1442,8 @@ function captureVideoSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, t
1442
1442
  const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
1443
1443
  ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
1444
1444
  ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
1445
+ const freezeCountDiff = (currentSsrcStats.freezeCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.freezeCount) || 0);
1446
+ ssrcMetrics.freezeRate = (freezeCountDiff * 1000) / timeDiff;
1445
1447
  }
1446
1448
  else {
1447
1449
  const kfCountDiff = currentSsrcStats.keyFramesEncoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.keyFramesEncoded) || 0);
@@ -1587,15 +1589,17 @@ const getOrCreateSsrcMetricsContainer = (statsByView, time, pcIndex, clientId, t
1587
1589
  ssrcStats.updated = time;
1588
1590
  return ssrcStats;
1589
1591
  };
1590
- const removeNonUpdatedStats = (statsByView, time) => {
1592
+ const removeNonUpdatedStats = (statsByView, time, renderedDimensionsByTrack) => {
1591
1593
  Object.entries(statsByView).forEach(([viewId, viewStats]) => {
1592
1594
  if (viewStats.updated !== undefined && viewStats.updated < time) {
1595
+ Object.keys(viewStats.tracks).forEach((trackId) => delete renderedDimensionsByTrack[trackId]);
1593
1596
  delete statsByView[viewId];
1594
1597
  }
1595
1598
  else {
1596
1599
  Object.entries(viewStats.tracks).forEach(([trackId, trackStats]) => {
1597
1600
  if (trackStats.updated < time) {
1598
1601
  delete viewStats.tracks[trackId];
1602
+ delete renderedDimensionsByTrack[trackId];
1599
1603
  }
1600
1604
  else {
1601
1605
  Object.entries(trackStats.ssrcs).forEach(([ssrc, ssrcStats]) => {
@@ -1633,7 +1637,7 @@ function collectStats(state_1, _a, immediate_1) {
1633
1637
  if (timeSinceLastUpdate < 400) {
1634
1638
  if (immediate)
1635
1639
  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); });
1640
+ 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
1641
  state.nextTimeout = setTimeout(collectStatsBound, interval);
1638
1642
  return;
1639
1643
  }
@@ -1726,7 +1730,7 @@ function collectStats(state_1, _a, immediate_1) {
1726
1730
  }
1727
1731
  });
1728
1732
  });
1729
- removeNonUpdatedStats(state.statsByView, state.lastUpdateTime);
1733
+ removeNonUpdatedStats(state.statsByView, state.lastUpdateTime, state.renderedDimensionsByTrack);
1730
1734
  Object.entries((defaultViewStats === null || defaultViewStats === void 0 ? void 0 : defaultViewStats.candidatePairs) || {}).forEach(([cpKey, cp]) => {
1731
1735
  const active = cp.lastRtcStatsTime === state.lastUpdateTime;
1732
1736
  cp.active = active;
@@ -1745,7 +1749,7 @@ function collectStats(state_1, _a, immediate_1) {
1745
1749
  return state.statsByView;
1746
1750
  }
1747
1751
  else {
1748
- state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
1752
+ 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
1753
  }
1750
1754
  }
1751
1755
  catch (e) {
@@ -1784,6 +1788,7 @@ const STATE = {
1784
1788
  currentMonitor: null,
1785
1789
  getClients: () => [],
1786
1790
  lastUpdateTime: 0,
1791
+ renderedDimensionsByTrack: {},
1787
1792
  statsByView: {},
1788
1793
  subscriptions: [],
1789
1794
  numFailedStatsReports: 0,
@@ -1802,6 +1807,9 @@ const getNumMissingTrackSsrcLookups = () => numMissingTrackSsrcLookups;
1802
1807
  const getNumFailedTrackSsrcLookups = () => numFailedTrackSsrcLookups;
1803
1808
  const getUpdatedStats = () => { var _a; return (_a = STATE.currentMonitor) === null || _a === void 0 ? void 0 : _a.getUpdatedStats(); };
1804
1809
  const setClientProvider = (provider) => (STATE.getClients = provider);
1810
+ const updateRenderedDimensions = (trackId, data) => {
1811
+ STATE.renderedDimensionsByTrack[trackId] = data;
1812
+ };
1805
1813
  function startStatsMonitor(state, { interval, logger }) {
1806
1814
  const collectStatsBound = collectStats.bind(null, state, { interval, logger });
1807
1815
  let cpuObserver;
@@ -2522,6 +2530,7 @@ class P2pRtcManager {
2522
2530
  this._features = features || {};
2523
2531
  this._isAudioOnlyMode = false;
2524
2532
  this._closed = false;
2533
+ this._videoTrackIdByStreamId = {};
2525
2534
  this._fetchMediaServersTimer = null;
2526
2535
  this._stopCameraTimeout = null;
2527
2536
  this._icePublicIPGatheringTimeoutID = null;
@@ -3002,6 +3011,8 @@ class P2pRtcManager {
3002
3011
  });
3003
3012
  return;
3004
3013
  }
3014
+ if (event.track.kind === "video")
3015
+ this._videoTrackIdByStreamId[stream.id] = event.track.id;
3005
3016
  if (session.streamIds.indexOf(stream.id) === -1) {
3006
3017
  session.streamIds.push(stream.id);
3007
3018
  this._emit(EVENTS.STREAM_ADDED, {
@@ -3432,7 +3443,11 @@ class P2pRtcManager {
3432
3443
  }, 60 * 1000);
3433
3444
  }
3434
3445
  }
3435
- updateStreamResolution() { }
3446
+ updateStreamResolution(streamId, _ignored, { width, height, }) {
3447
+ const trackId = this._videoTrackIdByStreamId[streamId];
3448
+ if (trackId)
3449
+ updateRenderedDimensions(trackId, { width, height, time: Date.now() });
3450
+ }
3436
3451
  stopOrResumeAudio() {
3437
3452
  }
3438
3453
  _handleStopOrResumeVideo({ enable, track }) {
@@ -5573,7 +5588,7 @@ class VegaRtcManager {
5573
5588
  this._syncIncomingStreamsWithPWA(clientId);
5574
5589
  }
5575
5590
  updateStreamResolution(streamId, _ignored, { width, height, }) {
5576
- var _a, _b;
5591
+ var _a, _b, _c;
5577
5592
  logger$2.info("updateStreamResolution()", { streamId, width, height });
5578
5593
  const consumerId = this._streamIdToVideoConsumerId.get(streamId);
5579
5594
  const consumer = this._consumers.get(consumerId);
@@ -5581,17 +5596,18 @@ class VegaRtcManager {
5581
5596
  this._streamIdToVideoResolution.set(streamId, { width, height });
5582
5597
  return;
5583
5598
  }
5599
+ updateRenderedDimensions((_a = consumer.track) === null || _a === void 0 ? void 0 : _a.id, { width, height, time: Date.now() });
5584
5600
  const numberOfActiveVideos = getNumberOfActiveVideos(this._consumers);
5585
5601
  const numberOfTemporalLayers = getNumberOfTemporalLayers(consumer);
5586
5602
  const { spatialLayer, temporalLayer } = getLayers({ width, height }, {
5587
5603
  numberOfActiveVideos,
5588
5604
  numberOfTemporalLayers,
5589
- uncappedSingleRemoteVideoOn: (_a = this._features) === null || _a === void 0 ? void 0 : _a.uncappedSingleRemoteVideoOn,
5605
+ uncappedSingleRemoteVideoOn: (_b = this._features) === null || _b === void 0 ? void 0 : _b.uncappedSingleRemoteVideoOn,
5590
5606
  });
5591
5607
  if (consumer.appData.spatialLayer !== spatialLayer || consumer.appData.temporalLayer !== temporalLayer) {
5592
5608
  consumer.appData.spatialLayer = spatialLayer;
5593
5609
  consumer.appData.temporalLayer = temporalLayer;
5594
- (_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.message("setConsumersPreferredLayers", {
5610
+ (_c = this._vegaConnection) === null || _c === void 0 ? void 0 : _c.message("setConsumersPreferredLayers", {
5595
5611
  consumerIds: [consumerId],
5596
5612
  spatialLayer,
5597
5613
  temporalLayer,
@@ -6006,6 +6022,92 @@ const getRoomMode = () => {
6006
6022
  return roomMode;
6007
6023
  };
6008
6024
 
6025
+ const createQualityDetector = (type) => {
6026
+ return {
6027
+ id: `quality-${type}`,
6028
+ enabled: ({ client }) => !client.isLocalClient && !client.isPresentation,
6029
+ check: ({ ssrc0, renderedDimensions, hasLiveTrack, kind }) => {
6030
+ if (!hasLiveTrack)
6031
+ return true;
6032
+ if (!ssrc0)
6033
+ return true;
6034
+ if (!ssrc0.bitrate)
6035
+ return true;
6036
+ if (kind === "video") {
6037
+ if (renderedDimensions && renderedDimensions.width && renderedDimensions.height) {
6038
+ const maxSideRendered = Math.max(renderedDimensions.width, renderedDimensions.height);
6039
+ const maxSideReceived = Math.max(ssrc0.width || 0, ssrc0.height || 0);
6040
+ const fpsReceived = ssrc0.fps || 0;
6041
+ const hadFreeze = !!ssrc0.freezeRate;
6042
+ const bitrate = ssrc0.bitrate || 0;
6043
+ if (maxSideRendered < 100) {
6044
+ if (type === "warning" && maxSideReceived < 160)
6045
+ return true;
6046
+ if (type === "critical" && maxSideReceived < 90)
6047
+ return true;
6048
+ if (type === "warning" && fpsReceived < 5)
6049
+ return true;
6050
+ if (type === "critical" && fpsReceived < 2)
6051
+ return true;
6052
+ if (type === "warning" && bitrate < 10000)
6053
+ return true;
6054
+ if (type === "critical" && bitrate < 5000)
6055
+ return true;
6056
+ }
6057
+ else if (maxSideRendered < 480) {
6058
+ if (type === "warning" && maxSideReceived < 240)
6059
+ return true;
6060
+ if (type === "critical" && maxSideReceived < 120)
6061
+ return true;
6062
+ if (type === "warning" && fpsReceived < 14)
6063
+ return true;
6064
+ if (type === "critical" && fpsReceived < 9)
6065
+ return true;
6066
+ if (hadFreeze)
6067
+ return true;
6068
+ if (type === "warning" && bitrate < 50000)
6069
+ return true;
6070
+ if (type === "critical" && bitrate < 20000)
6071
+ return true;
6072
+ }
6073
+ else {
6074
+ if (type === "warning" && maxSideReceived < 480)
6075
+ return true;
6076
+ if (type === "critical" && maxSideReceived < 240)
6077
+ return true;
6078
+ if (type === "warning" && fpsReceived < 14)
6079
+ return true;
6080
+ if (type === "critical" && fpsReceived < 9)
6081
+ return true;
6082
+ if (type === "warning" && bitrate < 200000)
6083
+ return true;
6084
+ if (type === "critical" && bitrate < 50000)
6085
+ return true;
6086
+ if (hadFreeze)
6087
+ return true;
6088
+ }
6089
+ }
6090
+ }
6091
+ else if (kind === "audio") {
6092
+ const audioLevel = ssrc0.audioLevel || 0;
6093
+ const concealment = ssrc0.audioConcealment || 0;
6094
+ const acceleration = ssrc0.audioAcceleration || 0;
6095
+ const deceleration = ssrc0.audioDeceleration || 0;
6096
+ const audioDistortion = concealment + acceleration + deceleration;
6097
+ if (audioLevel >= 0.01) {
6098
+ if (type === "warning" && audioDistortion > 0.1)
6099
+ return true;
6100
+ if (type === "critical" && audioDistortion > 0.2)
6101
+ return true;
6102
+ }
6103
+ }
6104
+ return false;
6105
+ },
6106
+ };
6107
+ };
6108
+ const qualityWarningDetector = createQualityDetector("warning");
6109
+ const qualityCriticalDetector = createQualityDetector("critical");
6110
+
6009
6111
  const badNetworkIssueDetector = {
6010
6112
  id: "bad-network",
6011
6113
  enabled: ({ hasLiveTrack, ssrcs }) => hasLiveTrack && ssrcs.length > 0,
@@ -6229,6 +6331,8 @@ const issueDetectors = [
6229
6331
  (ssrc0.audioLevel || 0) >= 0.001 &&
6230
6332
  (ssrc0.audioAcceleration || 0) >= 0.1,
6231
6333
  },
6334
+ qualityWarningDetector,
6335
+ qualityCriticalDetector,
6232
6336
  ];
6233
6337
 
6234
6338
  let subscriptions = [];
@@ -6383,7 +6487,7 @@ function issueDetectorOrMetricEnabled(issueDetectorOrMetric, checkData) {
6383
6487
  }
6384
6488
  return enabled && (issueDetectorOrMetric.enabled ? issueDetectorOrMetric.enabled(checkData) : true);
6385
6489
  }
6386
- function onUpdatedStats(statsByView, clients) {
6490
+ function onUpdatedStats(statsByView, clients, renderedDimensionsByTrack) {
6387
6491
  Object.values(aggregatedMetrics).forEach((metricData) => {
6388
6492
  metricData.curTicks = 0;
6389
6493
  metricData.curSum = 0;
@@ -6421,6 +6525,7 @@ function onUpdatedStats(statsByView, clients) {
6421
6525
  kind,
6422
6526
  track,
6423
6527
  trackStats,
6528
+ renderedDimensions: (track === null || track === void 0 ? void 0 : track.id) ? renderedDimensionsByTrack[track.id] : undefined,
6424
6529
  stats,
6425
6530
  hasLiveTrack,
6426
6531
  ssrc0,
@@ -7513,5 +7618,6 @@ exports.subscribeIssues = subscribeIssues;
7513
7618
  exports.subscribeStats = subscribeStats;
7514
7619
  exports.trackAnnotations = trackAnnotations;
7515
7620
  exports.turnServerOverride = turnServerOverride;
7621
+ exports.updateRenderedDimensions = updateRenderedDimensions;
7516
7622
  exports.variance = variance;
7517
7623
  //# 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;
@@ -2089,5 +2103,5 @@ declare class VegaRtcManager implements RtcManager {
2089
2103
  hasClient(clientId: string): boolean;
2090
2104
  }
2091
2105
 
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 };
2106
+ 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
2107
  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;
@@ -2089,5 +2103,5 @@ declare class VegaRtcManager implements RtcManager {
2089
2103
  hasClient(clientId: string): boolean;
2090
2104
  }
2091
2105
 
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 };
2106
+ 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
2107
  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;
@@ -2089,5 +2103,5 @@ declare class VegaRtcManager implements RtcManager {
2089
2103
  hasClient(clientId: string): boolean;
2090
2104
  }
2091
2105
 
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 };
2106
+ 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
2107
  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
@@ -1422,6 +1422,8 @@ function captureVideoSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, t
1422
1422
  const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
1423
1423
  ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
1424
1424
  ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
1425
+ const freezeCountDiff = (currentSsrcStats.freezeCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.freezeCount) || 0);
1426
+ ssrcMetrics.freezeRate = (freezeCountDiff * 1000) / timeDiff;
1425
1427
  }
1426
1428
  else {
1427
1429
  const kfCountDiff = currentSsrcStats.keyFramesEncoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.keyFramesEncoded) || 0);
@@ -1567,15 +1569,17 @@ const getOrCreateSsrcMetricsContainer = (statsByView, time, pcIndex, clientId, t
1567
1569
  ssrcStats.updated = time;
1568
1570
  return ssrcStats;
1569
1571
  };
1570
- const removeNonUpdatedStats = (statsByView, time) => {
1572
+ const removeNonUpdatedStats = (statsByView, time, renderedDimensionsByTrack) => {
1571
1573
  Object.entries(statsByView).forEach(([viewId, viewStats]) => {
1572
1574
  if (viewStats.updated !== undefined && viewStats.updated < time) {
1575
+ Object.keys(viewStats.tracks).forEach((trackId) => delete renderedDimensionsByTrack[trackId]);
1573
1576
  delete statsByView[viewId];
1574
1577
  }
1575
1578
  else {
1576
1579
  Object.entries(viewStats.tracks).forEach(([trackId, trackStats]) => {
1577
1580
  if (trackStats.updated < time) {
1578
1581
  delete viewStats.tracks[trackId];
1582
+ delete renderedDimensionsByTrack[trackId];
1579
1583
  }
1580
1584
  else {
1581
1585
  Object.entries(trackStats.ssrcs).forEach(([ssrc, ssrcStats]) => {
@@ -1613,7 +1617,7 @@ function collectStats(state_1, _a, immediate_1) {
1613
1617
  if (timeSinceLastUpdate < 400) {
1614
1618
  if (immediate)
1615
1619
  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); });
1620
+ 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
1621
  state.nextTimeout = setTimeout(collectStatsBound, interval);
1618
1622
  return;
1619
1623
  }
@@ -1706,7 +1710,7 @@ function collectStats(state_1, _a, immediate_1) {
1706
1710
  }
1707
1711
  });
1708
1712
  });
1709
- removeNonUpdatedStats(state.statsByView, state.lastUpdateTime);
1713
+ removeNonUpdatedStats(state.statsByView, state.lastUpdateTime, state.renderedDimensionsByTrack);
1710
1714
  Object.entries((defaultViewStats === null || defaultViewStats === void 0 ? void 0 : defaultViewStats.candidatePairs) || {}).forEach(([cpKey, cp]) => {
1711
1715
  const active = cp.lastRtcStatsTime === state.lastUpdateTime;
1712
1716
  cp.active = active;
@@ -1725,7 +1729,7 @@ function collectStats(state_1, _a, immediate_1) {
1725
1729
  return state.statsByView;
1726
1730
  }
1727
1731
  else {
1728
- state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
1732
+ 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
1733
  }
1730
1734
  }
1731
1735
  catch (e) {
@@ -1764,6 +1768,7 @@ const STATE = {
1764
1768
  currentMonitor: null,
1765
1769
  getClients: () => [],
1766
1770
  lastUpdateTime: 0,
1771
+ renderedDimensionsByTrack: {},
1767
1772
  statsByView: {},
1768
1773
  subscriptions: [],
1769
1774
  numFailedStatsReports: 0,
@@ -1782,6 +1787,9 @@ const getNumMissingTrackSsrcLookups = () => numMissingTrackSsrcLookups;
1782
1787
  const getNumFailedTrackSsrcLookups = () => numFailedTrackSsrcLookups;
1783
1788
  const getUpdatedStats = () => { var _a; return (_a = STATE.currentMonitor) === null || _a === void 0 ? void 0 : _a.getUpdatedStats(); };
1784
1789
  const setClientProvider = (provider) => (STATE.getClients = provider);
1790
+ const updateRenderedDimensions = (trackId, data) => {
1791
+ STATE.renderedDimensionsByTrack[trackId] = data;
1792
+ };
1785
1793
  function startStatsMonitor(state, { interval, logger }) {
1786
1794
  const collectStatsBound = collectStats.bind(null, state, { interval, logger });
1787
1795
  let cpuObserver;
@@ -2502,6 +2510,7 @@ class P2pRtcManager {
2502
2510
  this._features = features || {};
2503
2511
  this._isAudioOnlyMode = false;
2504
2512
  this._closed = false;
2513
+ this._videoTrackIdByStreamId = {};
2505
2514
  this._fetchMediaServersTimer = null;
2506
2515
  this._stopCameraTimeout = null;
2507
2516
  this._icePublicIPGatheringTimeoutID = null;
@@ -2982,6 +2991,8 @@ class P2pRtcManager {
2982
2991
  });
2983
2992
  return;
2984
2993
  }
2994
+ if (event.track.kind === "video")
2995
+ this._videoTrackIdByStreamId[stream.id] = event.track.id;
2985
2996
  if (session.streamIds.indexOf(stream.id) === -1) {
2986
2997
  session.streamIds.push(stream.id);
2987
2998
  this._emit(EVENTS.STREAM_ADDED, {
@@ -3412,7 +3423,11 @@ class P2pRtcManager {
3412
3423
  }, 60 * 1000);
3413
3424
  }
3414
3425
  }
3415
- updateStreamResolution() { }
3426
+ updateStreamResolution(streamId, _ignored, { width, height, }) {
3427
+ const trackId = this._videoTrackIdByStreamId[streamId];
3428
+ if (trackId)
3429
+ updateRenderedDimensions(trackId, { width, height, time: Date.now() });
3430
+ }
3416
3431
  stopOrResumeAudio() {
3417
3432
  }
3418
3433
  _handleStopOrResumeVideo({ enable, track }) {
@@ -5553,7 +5568,7 @@ class VegaRtcManager {
5553
5568
  this._syncIncomingStreamsWithPWA(clientId);
5554
5569
  }
5555
5570
  updateStreamResolution(streamId, _ignored, { width, height, }) {
5556
- var _a, _b;
5571
+ var _a, _b, _c;
5557
5572
  logger$2.info("updateStreamResolution()", { streamId, width, height });
5558
5573
  const consumerId = this._streamIdToVideoConsumerId.get(streamId);
5559
5574
  const consumer = this._consumers.get(consumerId);
@@ -5561,17 +5576,18 @@ class VegaRtcManager {
5561
5576
  this._streamIdToVideoResolution.set(streamId, { width, height });
5562
5577
  return;
5563
5578
  }
5579
+ updateRenderedDimensions((_a = consumer.track) === null || _a === void 0 ? void 0 : _a.id, { width, height, time: Date.now() });
5564
5580
  const numberOfActiveVideos = getNumberOfActiveVideos(this._consumers);
5565
5581
  const numberOfTemporalLayers = getNumberOfTemporalLayers(consumer);
5566
5582
  const { spatialLayer, temporalLayer } = getLayers({ width, height }, {
5567
5583
  numberOfActiveVideos,
5568
5584
  numberOfTemporalLayers,
5569
- uncappedSingleRemoteVideoOn: (_a = this._features) === null || _a === void 0 ? void 0 : _a.uncappedSingleRemoteVideoOn,
5585
+ uncappedSingleRemoteVideoOn: (_b = this._features) === null || _b === void 0 ? void 0 : _b.uncappedSingleRemoteVideoOn,
5570
5586
  });
5571
5587
  if (consumer.appData.spatialLayer !== spatialLayer || consumer.appData.temporalLayer !== temporalLayer) {
5572
5588
  consumer.appData.spatialLayer = spatialLayer;
5573
5589
  consumer.appData.temporalLayer = temporalLayer;
5574
- (_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.message("setConsumersPreferredLayers", {
5590
+ (_c = this._vegaConnection) === null || _c === void 0 ? void 0 : _c.message("setConsumersPreferredLayers", {
5575
5591
  consumerIds: [consumerId],
5576
5592
  spatialLayer,
5577
5593
  temporalLayer,
@@ -5986,6 +6002,92 @@ const getRoomMode = () => {
5986
6002
  return roomMode;
5987
6003
  };
5988
6004
 
6005
+ const createQualityDetector = (type) => {
6006
+ return {
6007
+ id: `quality-${type}`,
6008
+ enabled: ({ client }) => !client.isLocalClient && !client.isPresentation,
6009
+ check: ({ ssrc0, renderedDimensions, hasLiveTrack, kind }) => {
6010
+ if (!hasLiveTrack)
6011
+ return true;
6012
+ if (!ssrc0)
6013
+ return true;
6014
+ if (!ssrc0.bitrate)
6015
+ return true;
6016
+ if (kind === "video") {
6017
+ if (renderedDimensions && renderedDimensions.width && renderedDimensions.height) {
6018
+ const maxSideRendered = Math.max(renderedDimensions.width, renderedDimensions.height);
6019
+ const maxSideReceived = Math.max(ssrc0.width || 0, ssrc0.height || 0);
6020
+ const fpsReceived = ssrc0.fps || 0;
6021
+ const hadFreeze = !!ssrc0.freezeRate;
6022
+ const bitrate = ssrc0.bitrate || 0;
6023
+ if (maxSideRendered < 100) {
6024
+ if (type === "warning" && maxSideReceived < 160)
6025
+ return true;
6026
+ if (type === "critical" && maxSideReceived < 90)
6027
+ return true;
6028
+ if (type === "warning" && fpsReceived < 5)
6029
+ return true;
6030
+ if (type === "critical" && fpsReceived < 2)
6031
+ return true;
6032
+ if (type === "warning" && bitrate < 10000)
6033
+ return true;
6034
+ if (type === "critical" && bitrate < 5000)
6035
+ return true;
6036
+ }
6037
+ else if (maxSideRendered < 480) {
6038
+ if (type === "warning" && maxSideReceived < 240)
6039
+ return true;
6040
+ if (type === "critical" && maxSideReceived < 120)
6041
+ return true;
6042
+ if (type === "warning" && fpsReceived < 14)
6043
+ return true;
6044
+ if (type === "critical" && fpsReceived < 9)
6045
+ return true;
6046
+ if (hadFreeze)
6047
+ return true;
6048
+ if (type === "warning" && bitrate < 50000)
6049
+ return true;
6050
+ if (type === "critical" && bitrate < 20000)
6051
+ return true;
6052
+ }
6053
+ else {
6054
+ if (type === "warning" && maxSideReceived < 480)
6055
+ return true;
6056
+ if (type === "critical" && maxSideReceived < 240)
6057
+ return true;
6058
+ if (type === "warning" && fpsReceived < 14)
6059
+ return true;
6060
+ if (type === "critical" && fpsReceived < 9)
6061
+ return true;
6062
+ if (type === "warning" && bitrate < 200000)
6063
+ return true;
6064
+ if (type === "critical" && bitrate < 50000)
6065
+ return true;
6066
+ if (hadFreeze)
6067
+ return true;
6068
+ }
6069
+ }
6070
+ }
6071
+ else if (kind === "audio") {
6072
+ const audioLevel = ssrc0.audioLevel || 0;
6073
+ const concealment = ssrc0.audioConcealment || 0;
6074
+ const acceleration = ssrc0.audioAcceleration || 0;
6075
+ const deceleration = ssrc0.audioDeceleration || 0;
6076
+ const audioDistortion = concealment + acceleration + deceleration;
6077
+ if (audioLevel >= 0.01) {
6078
+ if (type === "warning" && audioDistortion > 0.1)
6079
+ return true;
6080
+ if (type === "critical" && audioDistortion > 0.2)
6081
+ return true;
6082
+ }
6083
+ }
6084
+ return false;
6085
+ },
6086
+ };
6087
+ };
6088
+ const qualityWarningDetector = createQualityDetector("warning");
6089
+ const qualityCriticalDetector = createQualityDetector("critical");
6090
+
5989
6091
  const badNetworkIssueDetector = {
5990
6092
  id: "bad-network",
5991
6093
  enabled: ({ hasLiveTrack, ssrcs }) => hasLiveTrack && ssrcs.length > 0,
@@ -6209,6 +6311,8 @@ const issueDetectors = [
6209
6311
  (ssrc0.audioLevel || 0) >= 0.001 &&
6210
6312
  (ssrc0.audioAcceleration || 0) >= 0.1,
6211
6313
  },
6314
+ qualityWarningDetector,
6315
+ qualityCriticalDetector,
6212
6316
  ];
6213
6317
 
6214
6318
  let subscriptions = [];
@@ -6363,7 +6467,7 @@ function issueDetectorOrMetricEnabled(issueDetectorOrMetric, checkData) {
6363
6467
  }
6364
6468
  return enabled && (issueDetectorOrMetric.enabled ? issueDetectorOrMetric.enabled(checkData) : true);
6365
6469
  }
6366
- function onUpdatedStats(statsByView, clients) {
6470
+ function onUpdatedStats(statsByView, clients, renderedDimensionsByTrack) {
6367
6471
  Object.values(aggregatedMetrics).forEach((metricData) => {
6368
6472
  metricData.curTicks = 0;
6369
6473
  metricData.curSum = 0;
@@ -6401,6 +6505,7 @@ function onUpdatedStats(statsByView, clients) {
6401
6505
  kind,
6402
6506
  track,
6403
6507
  trackStats,
6508
+ renderedDimensions: (track === null || track === void 0 ? void 0 : track.id) ? renderedDimensionsByTrack[track.id] : undefined,
6404
6509
  stats,
6405
6510
  hasLiveTrack,
6406
6511
  ssrc0,
@@ -7393,4 +7498,4 @@ var RtcEventNames;
7393
7498
  RtcEventNames["stream_added"] = "stream_added";
7394
7499
  })(RtcEventNames || (RtcEventNames = {}));
7395
7500
 
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 };
7501
+ 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 };
@@ -1422,6 +1422,8 @@ function captureVideoSsrcMetrics(ssrcMetrics, currentSsrcStats, prevSsrcStats, t
1422
1422
  const qpsumDiff = currentSsrcStats.qpSum - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.qpSum) || 0);
1423
1423
  ssrcMetrics.qpf = qpsumDiff / frameCountDiff;
1424
1424
  ssrcMetrics.fps = (frameCountDiff * 1000) / timeDiff;
1425
+ const freezeCountDiff = (currentSsrcStats.freezeCount || 0) - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.freezeCount) || 0);
1426
+ ssrcMetrics.freezeRate = (freezeCountDiff * 1000) / timeDiff;
1425
1427
  }
1426
1428
  else {
1427
1429
  const kfCountDiff = currentSsrcStats.keyFramesEncoded - ((prevSsrcStats === null || prevSsrcStats === void 0 ? void 0 : prevSsrcStats.keyFramesEncoded) || 0);
@@ -1567,15 +1569,17 @@ const getOrCreateSsrcMetricsContainer = (statsByView, time, pcIndex, clientId, t
1567
1569
  ssrcStats.updated = time;
1568
1570
  return ssrcStats;
1569
1571
  };
1570
- const removeNonUpdatedStats = (statsByView, time) => {
1572
+ const removeNonUpdatedStats = (statsByView, time, renderedDimensionsByTrack) => {
1571
1573
  Object.entries(statsByView).forEach(([viewId, viewStats]) => {
1572
1574
  if (viewStats.updated !== undefined && viewStats.updated < time) {
1575
+ Object.keys(viewStats.tracks).forEach((trackId) => delete renderedDimensionsByTrack[trackId]);
1573
1576
  delete statsByView[viewId];
1574
1577
  }
1575
1578
  else {
1576
1579
  Object.entries(viewStats.tracks).forEach(([trackId, trackStats]) => {
1577
1580
  if (trackStats.updated < time) {
1578
1581
  delete viewStats.tracks[trackId];
1582
+ delete renderedDimensionsByTrack[trackId];
1579
1583
  }
1580
1584
  else {
1581
1585
  Object.entries(trackStats.ssrcs).forEach(([ssrc, ssrcStats]) => {
@@ -1613,7 +1617,7 @@ function collectStats(state_1, _a, immediate_1) {
1613
1617
  if (timeSinceLastUpdate < 400) {
1614
1618
  if (immediate)
1615
1619
  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); });
1620
+ 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
1621
  state.nextTimeout = setTimeout(collectStatsBound, interval);
1618
1622
  return;
1619
1623
  }
@@ -1706,7 +1710,7 @@ function collectStats(state_1, _a, immediate_1) {
1706
1710
  }
1707
1711
  });
1708
1712
  });
1709
- removeNonUpdatedStats(state.statsByView, state.lastUpdateTime);
1713
+ removeNonUpdatedStats(state.statsByView, state.lastUpdateTime, state.renderedDimensionsByTrack);
1710
1714
  Object.entries((defaultViewStats === null || defaultViewStats === void 0 ? void 0 : defaultViewStats.candidatePairs) || {}).forEach(([cpKey, cp]) => {
1711
1715
  const active = cp.lastRtcStatsTime === state.lastUpdateTime;
1712
1716
  cp.active = active;
@@ -1725,7 +1729,7 @@ function collectStats(state_1, _a, immediate_1) {
1725
1729
  return state.statsByView;
1726
1730
  }
1727
1731
  else {
1728
- state.subscriptions.forEach((subscription) => { var _a; return (_a = subscription.onUpdatedStats) === null || _a === void 0 ? void 0 : _a.call(subscription, state.statsByView, clients); });
1732
+ 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
1733
  }
1730
1734
  }
1731
1735
  catch (e) {
@@ -1764,6 +1768,7 @@ const STATE = {
1764
1768
  currentMonitor: null,
1765
1769
  getClients: () => [],
1766
1770
  lastUpdateTime: 0,
1771
+ renderedDimensionsByTrack: {},
1767
1772
  statsByView: {},
1768
1773
  subscriptions: [],
1769
1774
  numFailedStatsReports: 0,
@@ -1782,6 +1787,9 @@ const getNumMissingTrackSsrcLookups = () => numMissingTrackSsrcLookups;
1782
1787
  const getNumFailedTrackSsrcLookups = () => numFailedTrackSsrcLookups;
1783
1788
  const getUpdatedStats = () => { var _a; return (_a = STATE.currentMonitor) === null || _a === void 0 ? void 0 : _a.getUpdatedStats(); };
1784
1789
  const setClientProvider = (provider) => (STATE.getClients = provider);
1790
+ const updateRenderedDimensions = (trackId, data) => {
1791
+ STATE.renderedDimensionsByTrack[trackId] = data;
1792
+ };
1785
1793
  function startStatsMonitor(state, { interval, logger }) {
1786
1794
  const collectStatsBound = collectStats.bind(null, state, { interval, logger });
1787
1795
  let cpuObserver;
@@ -2502,6 +2510,7 @@ class P2pRtcManager {
2502
2510
  this._features = features || {};
2503
2511
  this._isAudioOnlyMode = false;
2504
2512
  this._closed = false;
2513
+ this._videoTrackIdByStreamId = {};
2505
2514
  this._fetchMediaServersTimer = null;
2506
2515
  this._stopCameraTimeout = null;
2507
2516
  this._icePublicIPGatheringTimeoutID = null;
@@ -2982,6 +2991,8 @@ class P2pRtcManager {
2982
2991
  });
2983
2992
  return;
2984
2993
  }
2994
+ if (event.track.kind === "video")
2995
+ this._videoTrackIdByStreamId[stream.id] = event.track.id;
2985
2996
  if (session.streamIds.indexOf(stream.id) === -1) {
2986
2997
  session.streamIds.push(stream.id);
2987
2998
  this._emit(EVENTS.STREAM_ADDED, {
@@ -3412,7 +3423,11 @@ class P2pRtcManager {
3412
3423
  }, 60 * 1000);
3413
3424
  }
3414
3425
  }
3415
- updateStreamResolution() { }
3426
+ updateStreamResolution(streamId, _ignored, { width, height, }) {
3427
+ const trackId = this._videoTrackIdByStreamId[streamId];
3428
+ if (trackId)
3429
+ updateRenderedDimensions(trackId, { width, height, time: Date.now() });
3430
+ }
3416
3431
  stopOrResumeAudio() {
3417
3432
  }
3418
3433
  _handleStopOrResumeVideo({ enable, track }) {
@@ -5553,7 +5568,7 @@ class VegaRtcManager {
5553
5568
  this._syncIncomingStreamsWithPWA(clientId);
5554
5569
  }
5555
5570
  updateStreamResolution(streamId, _ignored, { width, height, }) {
5556
- var _a, _b;
5571
+ var _a, _b, _c;
5557
5572
  logger$2.info("updateStreamResolution()", { streamId, width, height });
5558
5573
  const consumerId = this._streamIdToVideoConsumerId.get(streamId);
5559
5574
  const consumer = this._consumers.get(consumerId);
@@ -5561,17 +5576,18 @@ class VegaRtcManager {
5561
5576
  this._streamIdToVideoResolution.set(streamId, { width, height });
5562
5577
  return;
5563
5578
  }
5579
+ updateRenderedDimensions((_a = consumer.track) === null || _a === void 0 ? void 0 : _a.id, { width, height, time: Date.now() });
5564
5580
  const numberOfActiveVideos = getNumberOfActiveVideos(this._consumers);
5565
5581
  const numberOfTemporalLayers = getNumberOfTemporalLayers(consumer);
5566
5582
  const { spatialLayer, temporalLayer } = getLayers({ width, height }, {
5567
5583
  numberOfActiveVideos,
5568
5584
  numberOfTemporalLayers,
5569
- uncappedSingleRemoteVideoOn: (_a = this._features) === null || _a === void 0 ? void 0 : _a.uncappedSingleRemoteVideoOn,
5585
+ uncappedSingleRemoteVideoOn: (_b = this._features) === null || _b === void 0 ? void 0 : _b.uncappedSingleRemoteVideoOn,
5570
5586
  });
5571
5587
  if (consumer.appData.spatialLayer !== spatialLayer || consumer.appData.temporalLayer !== temporalLayer) {
5572
5588
  consumer.appData.spatialLayer = spatialLayer;
5573
5589
  consumer.appData.temporalLayer = temporalLayer;
5574
- (_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.message("setConsumersPreferredLayers", {
5590
+ (_c = this._vegaConnection) === null || _c === void 0 ? void 0 : _c.message("setConsumersPreferredLayers", {
5575
5591
  consumerIds: [consumerId],
5576
5592
  spatialLayer,
5577
5593
  temporalLayer,
@@ -5986,6 +6002,92 @@ const getRoomMode = () => {
5986
6002
  return roomMode;
5987
6003
  };
5988
6004
 
6005
+ const createQualityDetector = (type) => {
6006
+ return {
6007
+ id: `quality-${type}`,
6008
+ enabled: ({ client }) => !client.isLocalClient && !client.isPresentation,
6009
+ check: ({ ssrc0, renderedDimensions, hasLiveTrack, kind }) => {
6010
+ if (!hasLiveTrack)
6011
+ return true;
6012
+ if (!ssrc0)
6013
+ return true;
6014
+ if (!ssrc0.bitrate)
6015
+ return true;
6016
+ if (kind === "video") {
6017
+ if (renderedDimensions && renderedDimensions.width && renderedDimensions.height) {
6018
+ const maxSideRendered = Math.max(renderedDimensions.width, renderedDimensions.height);
6019
+ const maxSideReceived = Math.max(ssrc0.width || 0, ssrc0.height || 0);
6020
+ const fpsReceived = ssrc0.fps || 0;
6021
+ const hadFreeze = !!ssrc0.freezeRate;
6022
+ const bitrate = ssrc0.bitrate || 0;
6023
+ if (maxSideRendered < 100) {
6024
+ if (type === "warning" && maxSideReceived < 160)
6025
+ return true;
6026
+ if (type === "critical" && maxSideReceived < 90)
6027
+ return true;
6028
+ if (type === "warning" && fpsReceived < 5)
6029
+ return true;
6030
+ if (type === "critical" && fpsReceived < 2)
6031
+ return true;
6032
+ if (type === "warning" && bitrate < 10000)
6033
+ return true;
6034
+ if (type === "critical" && bitrate < 5000)
6035
+ return true;
6036
+ }
6037
+ else if (maxSideRendered < 480) {
6038
+ if (type === "warning" && maxSideReceived < 240)
6039
+ return true;
6040
+ if (type === "critical" && maxSideReceived < 120)
6041
+ return true;
6042
+ if (type === "warning" && fpsReceived < 14)
6043
+ return true;
6044
+ if (type === "critical" && fpsReceived < 9)
6045
+ return true;
6046
+ if (hadFreeze)
6047
+ return true;
6048
+ if (type === "warning" && bitrate < 50000)
6049
+ return true;
6050
+ if (type === "critical" && bitrate < 20000)
6051
+ return true;
6052
+ }
6053
+ else {
6054
+ if (type === "warning" && maxSideReceived < 480)
6055
+ return true;
6056
+ if (type === "critical" && maxSideReceived < 240)
6057
+ return true;
6058
+ if (type === "warning" && fpsReceived < 14)
6059
+ return true;
6060
+ if (type === "critical" && fpsReceived < 9)
6061
+ return true;
6062
+ if (type === "warning" && bitrate < 200000)
6063
+ return true;
6064
+ if (type === "critical" && bitrate < 50000)
6065
+ return true;
6066
+ if (hadFreeze)
6067
+ return true;
6068
+ }
6069
+ }
6070
+ }
6071
+ else if (kind === "audio") {
6072
+ const audioLevel = ssrc0.audioLevel || 0;
6073
+ const concealment = ssrc0.audioConcealment || 0;
6074
+ const acceleration = ssrc0.audioAcceleration || 0;
6075
+ const deceleration = ssrc0.audioDeceleration || 0;
6076
+ const audioDistortion = concealment + acceleration + deceleration;
6077
+ if (audioLevel >= 0.01) {
6078
+ if (type === "warning" && audioDistortion > 0.1)
6079
+ return true;
6080
+ if (type === "critical" && audioDistortion > 0.2)
6081
+ return true;
6082
+ }
6083
+ }
6084
+ return false;
6085
+ },
6086
+ };
6087
+ };
6088
+ const qualityWarningDetector = createQualityDetector("warning");
6089
+ const qualityCriticalDetector = createQualityDetector("critical");
6090
+
5989
6091
  const badNetworkIssueDetector = {
5990
6092
  id: "bad-network",
5991
6093
  enabled: ({ hasLiveTrack, ssrcs }) => hasLiveTrack && ssrcs.length > 0,
@@ -6209,6 +6311,8 @@ const issueDetectors = [
6209
6311
  (ssrc0.audioLevel || 0) >= 0.001 &&
6210
6312
  (ssrc0.audioAcceleration || 0) >= 0.1,
6211
6313
  },
6314
+ qualityWarningDetector,
6315
+ qualityCriticalDetector,
6212
6316
  ];
6213
6317
 
6214
6318
  let subscriptions = [];
@@ -6363,7 +6467,7 @@ function issueDetectorOrMetricEnabled(issueDetectorOrMetric, checkData) {
6363
6467
  }
6364
6468
  return enabled && (issueDetectorOrMetric.enabled ? issueDetectorOrMetric.enabled(checkData) : true);
6365
6469
  }
6366
- function onUpdatedStats(statsByView, clients) {
6470
+ function onUpdatedStats(statsByView, clients, renderedDimensionsByTrack) {
6367
6471
  Object.values(aggregatedMetrics).forEach((metricData) => {
6368
6472
  metricData.curTicks = 0;
6369
6473
  metricData.curSum = 0;
@@ -6401,6 +6505,7 @@ function onUpdatedStats(statsByView, clients) {
6401
6505
  kind,
6402
6506
  track,
6403
6507
  trackStats,
6508
+ renderedDimensions: (track === null || track === void 0 ? void 0 : track.id) ? renderedDimensionsByTrack[track.id] : undefined,
6404
6509
  stats,
6405
6510
  hasLiveTrack,
6406
6511
  ssrc0,
@@ -7393,5 +7498,5 @@ var RtcEventNames;
7393
7498
  RtcEventNames["stream_added"] = "stream_added";
7394
7499
  })(RtcEventNames || (RtcEventNames = {}));
7395
7500
 
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 };
7501
+ 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
7502
  //# 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.0",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/whereby/sdk",
7
7
  "repository": {