@unboundcx/video-sdk-client 2.0.11 → 2.0.12
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/VideoMeetingClient.js
CHANGED
|
@@ -829,6 +829,10 @@ export class VideoMeetingClient extends EventEmitter {
|
|
|
829
829
|
profile.maxSpatialLayer,
|
|
830
830
|
profile.maxTemporalLayer,
|
|
831
831
|
);
|
|
832
|
+
// Keep the send-cap flag in sync on every level change so QualityMonitor
|
|
833
|
+
// discounts the BWE floor while we're intentionally throttled — even on
|
|
834
|
+
// the mid-call path that doesn't re-produce.
|
|
835
|
+
this.mediasoup.setSendBitrateCap(profile.sendMaxBitrate || 0);
|
|
832
836
|
}
|
|
833
837
|
if (this.remoteMedia) {
|
|
834
838
|
this.remoteMedia.setLayerCeiling(
|
|
@@ -38,6 +38,12 @@ export class MediasoupManager extends EventEmitter {
|
|
|
38
38
|
// no cap. See videoQualityProfiles.js + RemoteMediaManager.setLayerCeiling.
|
|
39
39
|
this.layerCeiling = { maxSpatialLayer: null, maxTemporalLayer: null };
|
|
40
40
|
|
|
41
|
+
// Active send bitrate cap (bps) from the data-saver profile, or 0 when
|
|
42
|
+
// uncapped. QualityMonitor reads this so it can ignore Chrome's BWE
|
|
43
|
+
// "available upload" floor while we're INTENTIONALLY sending low — a low
|
|
44
|
+
// estimate then means "no demand to probe higher", not "bad network".
|
|
45
|
+
this.sendBitrateCap = 0;
|
|
46
|
+
|
|
41
47
|
// Initialize stats collector
|
|
42
48
|
this.statsCollector = new StatsCollector(this.logger);
|
|
43
49
|
this.virtualBackgroundStore = null; // Will be set externally
|
|
@@ -684,6 +690,9 @@ export class MediasoupManager extends EventEmitter {
|
|
|
684
690
|
// gets the lion's share; lower layers get proportionally less.
|
|
685
691
|
// (Screen share never reaches here.)
|
|
686
692
|
const maxBitrate = Number(options.maxBitrate) || 0;
|
|
693
|
+
// Remember the active send cap so QualityMonitor can discount the BWE
|
|
694
|
+
// floor while we're intentionally throttled (data saver).
|
|
695
|
+
this.sendBitrateCap = maxBitrate;
|
|
687
696
|
if (maxBitrate > 0 && produceOptions.encodings?.length) {
|
|
688
697
|
// Relative weights per rid; we normalize over the layers actually
|
|
689
698
|
// present so the allocated total always equals the budget whether
|
|
@@ -1101,6 +1110,21 @@ export class MediasoupManager extends EventEmitter {
|
|
|
1101
1110
|
return this.producers.get("video") || this.producers.get("camera") || null;
|
|
1102
1111
|
}
|
|
1103
1112
|
|
|
1113
|
+
/**
|
|
1114
|
+
* Set/clear the active send bitrate cap (bps; 0 = uncapped). Lets the
|
|
1115
|
+
* mid-call data-saver path (setParameters, no re-produce) keep
|
|
1116
|
+
* sendBitrateCap in sync so QualityMonitor discounts the BWE floor.
|
|
1117
|
+
* @param {number} bps
|
|
1118
|
+
*/
|
|
1119
|
+
setSendBitrateCap(bps) {
|
|
1120
|
+
this.sendBitrateCap = Number(bps) || 0;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/** @returns {number} active send bitrate cap in bps (0 = uncapped) */
|
|
1124
|
+
getSendBitrateCap() {
|
|
1125
|
+
return this.sendBitrateCap || 0;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1104
1128
|
/**
|
|
1105
1129
|
* Ask the SFU to forward a lower simulcast layer of the given peer's
|
|
1106
1130
|
* video producer to us. spatialLayer 0=low, 1=mid, 2=high.
|
|
@@ -423,15 +423,27 @@ export class QualityMonitor {
|
|
|
423
423
|
// counting that as a bad network wrongly marks audio-only participants
|
|
424
424
|
// "poor" and triggers a disable-camera suggestion for a camera that's
|
|
425
425
|
// already off. RTT + loss remain valid either way.
|
|
426
|
-
//
|
|
427
|
-
//
|
|
426
|
+
//
|
|
427
|
+
// The SAME artifact happens under data saver: when we deliberately cap the
|
|
428
|
+
// send to e.g. 600kbps and a low-motion scene only uses ~30kbps, the BWE
|
|
429
|
+
// collapses toward what we're actually sending (no demand to probe higher),
|
|
430
|
+
// so availMbps reads ~0.1 even on a perfectly healthy link. So when a send
|
|
431
|
+
// cap is active we IGNORE the availMbps floor entirely and rely only on the
|
|
432
|
+
// encoder-bandwidth-limited ratio, which genuinely means "the encoder wants
|
|
433
|
+
// more bits than the link will give" — the real signal that survives a cap.
|
|
434
|
+
const sendCapped =
|
|
435
|
+
typeof this.mediasoupManager?.getSendBitrateCap === "function" &&
|
|
436
|
+
this.mediasoupManager.getSendBitrateCap() > 0;
|
|
437
|
+
const useAvailFloor = this._isSendingVideo() && !sendCapped;
|
|
428
438
|
const bandwidthSeverity = !this._isSendingVideo()
|
|
429
439
|
? 0
|
|
430
|
-
: availMbps > 0 && availMbps <= this.thresholds.availMbpsCrit
|
|
440
|
+
: useAvailFloor && availMbps > 0 && availMbps <= this.thresholds.availMbpsCrit
|
|
431
441
|
? 2
|
|
432
442
|
: bwLimitedRatio >= this.thresholds.encoderBwLimitedCritRatio
|
|
433
443
|
? 2
|
|
434
|
-
:
|
|
444
|
+
: useAvailFloor &&
|
|
445
|
+
availMbps > 0 &&
|
|
446
|
+
availMbps <= this.thresholds.availMbpsWarn
|
|
435
447
|
? 1
|
|
436
448
|
: bwLimitedRatio >= this.thresholds.encoderBwLimitedWarnRatio
|
|
437
449
|
? 1
|