|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+/* global config */
|
|
1
|
2
|
import EventEmitter from "events";
|
|
2
|
3
|
|
|
3
|
4
|
import CQEvents from "../../service/connectionquality/CQEvents";
|
|
|
@@ -35,6 +36,58 @@ function calculateQuality(newVal, oldVal) {
|
|
35
|
36
|
return (newVal <= oldVal) ? newVal : (9*oldVal + newVal) / 10;
|
|
36
|
37
|
}
|
|
37
|
38
|
|
|
|
39
|
+// webrtc table describing simulcast resolutions and used bandwidth
|
|
|
40
|
+// https://chromium.googlesource.com/external/webrtc/+/master/webrtc/media/engine/simulcast.cc#42
|
|
|
41
|
+var _bandwidthMap = [
|
|
|
42
|
+ { width: 1920, height: 1080, layers:3, max: 5000, min: 800 },
|
|
|
43
|
+ { width: 1280, height: 720, layers:3, max: 2500, min: 600 },
|
|
|
44
|
+ { width: 960, height: 540, layers:3, max: 900, min: 450 },
|
|
|
45
|
+ { width: 640, height: 360, layers:2, max: 700, min: 150 },
|
|
|
46
|
+ { width: 480, height: 270, layers:2, max: 450, min: 150 },
|
|
|
47
|
+ { width: 320, height: 180, layers:1, max: 200, min: 30 }
|
|
|
48
|
+];
|
|
|
49
|
+
|
|
|
50
|
+/**
|
|
|
51
|
+ * We disable quality calculations based on bandwidth if simulcast is disabled,
|
|
|
52
|
+ * or enable it in case of no simulcast and we force it.
|
|
|
53
|
+ * @type {boolean}
|
|
|
54
|
+ */
|
|
|
55
|
+var disableQualityBasedOnBandwidth =
|
|
|
56
|
+ config.forceQualityBasedOnBandwidth ? false : config.disableSimulcast;
|
|
|
57
|
+
|
|
|
58
|
+/**
|
|
|
59
|
+ * Calculates the quality percentage based on the input resolution height and
|
|
|
60
|
+ * the upload reported by the client. The value is based on the interval from
|
|
|
61
|
+ * _bandwidthMap.
|
|
|
62
|
+ * @param inputHeight the resolution used to open the camera.
|
|
|
63
|
+ * @param upload the upload rate reported by client.
|
|
|
64
|
+ * @returns {*} the percent of upload based on _bandwidthMap and maximum value
|
|
|
65
|
+ * of 100, as values of the map are approximate and clients can stream above
|
|
|
66
|
+ * those values.
|
|
|
67
|
+ */
|
|
|
68
|
+function calculateQualityUsingUpload(inputHeight, upload) {
|
|
|
69
|
+ let foundResolution = null;
|
|
|
70
|
+
|
|
|
71
|
+ for (let i in _bandwidthMap) {
|
|
|
72
|
+ let r = _bandwidthMap[i];
|
|
|
73
|
+ if (r.height <= inputHeight) {
|
|
|
74
|
+ foundResolution = r;
|
|
|
75
|
+ break;
|
|
|
76
|
+ }
|
|
|
77
|
+ }
|
|
|
78
|
+
|
|
|
79
|
+ if (!foundResolution)
|
|
|
80
|
+ return false;
|
|
|
81
|
+
|
|
|
82
|
+ if (upload <= foundResolution.min)
|
|
|
83
|
+ return 0;
|
|
|
84
|
+
|
|
|
85
|
+ return Math.min(
|
|
|
86
|
+ ((upload - foundResolution.min)*100)
|
|
|
87
|
+ / (foundResolution.max - foundResolution.min),
|
|
|
88
|
+ 100);
|
|
|
89
|
+}
|
|
|
90
|
+
|
|
38
|
91
|
export default {
|
|
39
|
92
|
/**
|
|
40
|
93
|
* Updates the local statistics
|
|
|
@@ -42,15 +95,28 @@ export default {
|
|
42
|
95
|
* @param dontUpdateLocalConnectionQuality {boolean} if true -
|
|
43
|
96
|
* localConnectionQuality wont be recalculated.
|
|
44
|
97
|
*/
|
|
45
|
|
- updateLocalStats: function (data, dontUpdateLocalConnectionQuality) {
|
|
46
|
|
- stats = data;
|
|
47
|
|
- if(!dontUpdateLocalConnectionQuality) {
|
|
48
|
|
- var newVal = 100 - stats.packetLoss.total;
|
|
49
|
|
- localConnectionQuality =
|
|
50
|
|
- calculateQuality(newVal, localConnectionQuality);
|
|
51
|
|
- }
|
|
52
|
|
- eventEmitter.emit(CQEvents.LOCALSTATS_UPDATED, localConnectionQuality,
|
|
53
|
|
- stats);
|
|
|
98
|
+ updateLocalStats:
|
|
|
99
|
+ function (data, dontUpdateLocalConnectionQuality, localVideo) {
|
|
|
100
|
+ stats = data;
|
|
|
101
|
+ if(!dontUpdateLocalConnectionQuality) {
|
|
|
102
|
+ if (!disableQualityBasedOnBandwidth
|
|
|
103
|
+ && !localVideo.isMuted()
|
|
|
104
|
+ && localVideo.videoType !== 'desktop'
|
|
|
105
|
+ && localVideo.resolution) {
|
|
|
106
|
+ let val = calculateQualityUsingUpload(
|
|
|
107
|
+ localVideo.resolution,
|
|
|
108
|
+ data.bitrate.upload);
|
|
|
109
|
+ if (val) {
|
|
|
110
|
+ localConnectionQuality = val;
|
|
|
111
|
+ }
|
|
|
112
|
+ } else {
|
|
|
113
|
+ var newVal = 100 - stats.packetLoss.total;
|
|
|
114
|
+ localConnectionQuality =
|
|
|
115
|
+ calculateQuality(newVal, localConnectionQuality);
|
|
|
116
|
+ }
|
|
|
117
|
+ }
|
|
|
118
|
+ eventEmitter.emit(
|
|
|
119
|
+ CQEvents.LOCALSTATS_UPDATED, localConnectionQuality, stats);
|
|
54
|
120
|
},
|
|
55
|
121
|
|
|
56
|
122
|
/**
|
|
|
@@ -68,23 +134,41 @@ export default {
|
|
68
|
134
|
* @param id the id associated with the statistics
|
|
69
|
135
|
* @param data the statistics
|
|
70
|
136
|
*/
|
|
71
|
|
- updateRemoteStats: function (id, data) {
|
|
72
|
|
- if (!data || !("packetLoss" in data) || !("total" in data.packetLoss)) {
|
|
73
|
|
- eventEmitter.emit(CQEvents.REMOTESTATS_UPDATED, id, null, null);
|
|
74
|
|
- return;
|
|
75
|
|
- }
|
|
76
|
|
- // Use only the fields we need
|
|
77
|
|
- data = {bitrate: data.bitrate, packetLoss: data.packetLoss};
|
|
|
137
|
+ updateRemoteStats:
|
|
|
138
|
+ function (id, data, remoteVideoType, isRemoteVideoMuted) {
|
|
|
139
|
+ if (!data ||
|
|
|
140
|
+ !("packetLoss" in data) ||
|
|
|
141
|
+ !("total" in data.packetLoss)) {
|
|
|
142
|
+ eventEmitter.emit(CQEvents.REMOTESTATS_UPDATED, id, null, null);
|
|
|
143
|
+ return;
|
|
|
144
|
+ }
|
|
|
145
|
+
|
|
|
146
|
+ let inputResolution = data.resolution;
|
|
|
147
|
+ // Use only the fields we need
|
|
|
148
|
+ data = {bitrate: data.bitrate, packetLoss: data.packetLoss};
|
|
78
|
149
|
|
|
79
|
|
- remoteStats[id] = data;
|
|
|
150
|
+ remoteStats[id] = data;
|
|
80
|
151
|
|
|
81
|
|
- var newVal = 100 - data.packetLoss.total;
|
|
82
|
|
- var oldVal = remoteConnectionQuality[id];
|
|
83
|
|
- remoteConnectionQuality[id] = calculateQuality(newVal, oldVal || 100);
|
|
|
152
|
+ if (disableQualityBasedOnBandwidth
|
|
|
153
|
+ || isRemoteVideoMuted
|
|
|
154
|
+ || remoteVideoType === 'desktop'
|
|
|
155
|
+ || !inputResolution) {
|
|
|
156
|
+ var newVal = 100 - data.packetLoss.total;
|
|
|
157
|
+ var oldVal = remoteConnectionQuality[id];
|
|
|
158
|
+ remoteConnectionQuality[id]
|
|
|
159
|
+ = calculateQuality(newVal, oldVal || 100);
|
|
|
160
|
+ } else {
|
|
|
161
|
+ let val = calculateQualityUsingUpload(
|
|
|
162
|
+ inputResolution.inputHeight,
|
|
|
163
|
+ data.bitrate.upload);
|
|
|
164
|
+ if (val) {
|
|
|
165
|
+ remoteConnectionQuality[id] = val;
|
|
|
166
|
+ }
|
|
|
167
|
+ }
|
|
84
|
168
|
|
|
85
|
|
- eventEmitter.emit(
|
|
86
|
|
- CQEvents.REMOTESTATS_UPDATED, id, remoteConnectionQuality[id],
|
|
87
|
|
- remoteStats[id]);
|
|
|
169
|
+ eventEmitter.emit(
|
|
|
170
|
+ CQEvents.REMOTESTATS_UPDATED, id,
|
|
|
171
|
+ remoteConnectionQuality[id], remoteStats[id]);
|
|
88
|
172
|
},
|
|
89
|
173
|
|
|
90
|
174
|
/**
|