Pārlūkot izejas kodu

feat: Moves connectionquality to lib-jitsi-meet.

dev1
Boris Grozev 9 gadus atpakaļ
vecāks
revīzija
edf6a3a1c2

+ 3
- 0
JitsiConference.js Parādīt failu

@@ -21,6 +21,7 @@ var Transcriber = require("./modules/transcription/transcriber");
21 21
 import ParticipantConnectionStatus
22 22
     from "./modules/connectivity/ParticipantConnectionStatus";
23 23
 import TalkMutedDetection from "./modules/TalkMutedDetection";
24
+import ConnectionQuality from "./modules/connectivity/ConnectionQuality";
24 25
 
25 26
 /**
26 27
  * Creates a JitsiConference object with the given name and properties.
@@ -64,6 +65,8 @@ function JitsiConference(options) {
64 65
     // We need to know if the potential issue happened before or after
65 66
     // the restart.
66 67
     this.wasStopped = false;
68
+    this.connectionQuality
69
+        = new ConnectionQuality(this, this.eventEmitter, options);
67 70
 }
68 71
 
69 72
 /**

+ 3
- 1
JitsiMeetJS.js Parādīt failu

@@ -7,6 +7,7 @@ import * as JitsiConferenceEvents from "./JitsiConferenceEvents";
7 7
 import * as JitsiConnectionErrors from "./JitsiConnectionErrors";
8 8
 import * as JitsiConnectionEvents from "./JitsiConnectionEvents";
9 9
 import * as JitsiMediaDevicesEvents from "./JitsiMediaDevicesEvents";
10
+import * as ConnectionQualityEvents from "./service/connectivity/ConnectionQualityEvents";
10 11
 import JitsiTrackError from "./JitsiTrackError";
11 12
 import * as JitsiTrackErrors from "./JitsiTrackErrors";
12 13
 import * as JitsiTrackEvents from "./JitsiTrackEvents";
@@ -76,7 +77,8 @@ var LibJitsiMeet = {
76 77
         conference: JitsiConferenceEvents,
77 78
         connection: JitsiConnectionEvents,
78 79
         track: JitsiTrackEvents,
79
-        mediaDevices: JitsiMediaDevicesEvents
80
+        mediaDevices: JitsiMediaDevicesEvents,
81
+        connectionQuality: ConnectionQualityEvents
80 82
     },
81 83
     errors: {
82 84
         conference: JitsiConferenceErrors,

+ 213
- 0
modules/connectivity/ConnectionQuality.js Parādīt failu

@@ -0,0 +1,213 @@
1
+import * as ConnectionQualityEvents
2
+    from "../../service/connectivity/ConnectionQualityEvents";
3
+import * as ConferenceEvents from "../../JitsiConferenceEvents";
4
+
5
+// webrtc table describing simulcast resolutions and used bandwidth
6
+// https://chromium.googlesource.com/external/webrtc/+/master/webrtc/media/engine/simulcast.cc#42
7
+const _bandwidthMap = [
8
+    { width: 1920, height: 1080, layers:3, max: 5000, min: 800 },
9
+    { width: 1280, height: 720,  layers:3, max: 2500, min: 600 },
10
+    { width: 960,  height: 540,  layers:3, max: 900,  min: 450 },
11
+    { width: 640,  height: 360,  layers:2, max: 700,  min: 150 },
12
+    { width: 480,  height: 270,  layers:2, max: 450,  min: 150 },
13
+    { width: 320,  height: 180,  layers:1, max: 200,  min: 30 }
14
+];
15
+
16
+/**
17
+ * Calculates the quality percent based on passed new and old value.
18
+ * @param newVal the new value
19
+ * @param oldVal the old value
20
+ */
21
+function calculateQuality(newVal, oldVal) {
22
+    return (newVal <= oldVal) ? newVal : (9*oldVal + newVal) / 10;
23
+}
24
+
25
+/**
26
+ * Calculates the quality percentage based on the input resolution height and
27
+ * the upload reported by the client. The value is based on the interval from
28
+ * _bandwidthMap.
29
+ * @param inputHeight the resolution used to open the camera.
30
+ * @param upload the upload rate reported by client.
31
+ * @returns {int} the percent of upload based on _bandwidthMap and maximum value
32
+ * of 100, as values of the map are approximate and clients can stream above
33
+ * those values. Returns undefined if no result is found.
34
+ */
35
+function calculateQualityUsingUpload(inputHeight, upload) {
36
+    // found resolution from _bandwidthMap which height is equal or less than
37
+    // the inputHeight
38
+    let foundResolution = _bandwidthMap.find((r) => (r.height <= inputHeight));
39
+
40
+    if (!foundResolution)
41
+        return undefined;
42
+
43
+    if (upload <= foundResolution.min)
44
+        return 0;
45
+
46
+    return Math.min(
47
+        ((upload - foundResolution.min)*100)
48
+            / (foundResolution.max - foundResolution.min),
49
+        100);
50
+}
51
+
52
+export default class ConnectionQuality {
53
+    constructor(conference, eventEmitter, options) {
54
+        this.eventEmitter = eventEmitter;
55
+
56
+        this.disableQualityBasedOnBandwidth =
57
+            options.forceQualityBasedOnBandwidth
58
+                    ? false : !!options.disableSimulcast;
59
+        /**
60
+         * local stats
61
+         * @type {{}}
62
+         */
63
+        this.localStats = {};
64
+
65
+        /**
66
+         * remote stats
67
+         * @type {{}}
68
+         */
69
+        this.remoteStats = {};
70
+
71
+        /**
72
+         * Quality percent( 100% - good, 0% - bad.) for the local user.
73
+         */
74
+        this.localConnectionQuality = 100;
75
+
76
+        /**
77
+         * Quality percent( 100% - good, 0% - bad.) stored per id.
78
+         */
79
+        this.remoteConnectionQuality = {};
80
+
81
+        conference.on(ConferenceEvents.CONNECTION_INTERRUPTED,
82
+                      () => { this._updateLocalConnectionQuality(0); });
83
+
84
+        conference.on(ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
85
+            (participant, payload) => {
86
+                // TODO move "stats" to a constant.
87
+                if(payload.type === "stats") {
88
+                    let remoteVideo = participant.getTracks()
89
+                        .find(tr => tr.isVideoTrack());
90
+                    this.updateRemoteStats(
91
+                        participant.getId(),
92
+                        payload.values,
93
+                        remoteVideo ? remoteVideo.videoType : undefined,
94
+                        remoteVideo ? remoteVideo.isMuted() : undefined);
95
+                }
96
+        });
97
+    }
98
+
99
+    /**
100
+     * Returns the new quality value based on the input parameters.
101
+     * Used to calculate remote and local values.
102
+     * @param data the data
103
+     * @param lastQualityValue the last value we calculated
104
+     * @param videoType need to check whether we are screen sharing
105
+     * @param isMuted is video muted
106
+     * @param resolution the input resolution used by the camera
107
+     * @returns {*} the newly calculated value or undefined if no result
108
+     * @private
109
+     */
110
+    _getNewQualityValue(
111
+        data, lastQualityValue, videoType, isMuted, resolution) {
112
+        if (this.disableQualityBasedOnBandwidth
113
+            || isMuted
114
+            || videoType === 'desktop'
115
+            || !resolution) {
116
+            return calculateQuality(
117
+                100 - data.packetLoss.total,
118
+                lastQualityValue || 100);
119
+        } else {
120
+            return calculateQualityUsingUpload(
121
+                resolution,
122
+                data.bitrate.upload);
123
+        }
124
+    }
125
+
126
+    /**
127
+     * Updates only the localConnectionQuality value
128
+     * @param values {int} the new value. should be from 0 - 100.
129
+     */
130
+    _updateLocalConnectionQuality(value) {
131
+        this.localConnectionQuality = value;
132
+        this.eventEmitter.emit(
133
+            ConnectionQualityEvents.LOCAL_STATS_UPDATED,
134
+            this.localConnectionQuality,
135
+            this.localStats);
136
+    }
137
+
138
+    /**
139
+     * Updates the local statistics
140
+     * @param data new statistics
141
+     * @param dontUpdateLocalConnectionQuality {boolean} if true -
142
+     * localConnectionQuality wont be recalculated.
143
+     * @param videoType the local video type
144
+     * @param isMuted current state of local video, whether it is muted
145
+     * @param resolution the current resolution used by local video
146
+     */
147
+    updateLocalStats(data, dontUpdateLocalConnectionQuality,
148
+                  videoType, isMuted, resolution) {
149
+            this.localStats = data;
150
+            if(!dontUpdateLocalConnectionQuality) {
151
+                let val = this._getNewQualityValue(
152
+                    this.localStats,
153
+                    this.localConnectionQuality,
154
+                    videoType,
155
+                    isMuted,
156
+                    resolution);
157
+                if (val !== undefined)
158
+                    this.localConnectionQuality = val;
159
+            }
160
+            this.eventEmitter.emit(
161
+                ConnectionQualityEvents.LOCAL_STATS_UPDATED,
162
+                this.localConnectionQuality,
163
+                this.localStats);
164
+    }
165
+
166
+    /**
167
+     * Updates remote statistics
168
+     * @param id the id associated with the statistics
169
+     * @param data the statistics received
170
+     * @param remoteVideoType the video type of the remote video
171
+     * @param isRemoteVideoMuted whether remote video is muted
172
+     */
173
+    updateRemoteStats(id, data, remoteVideoType, isRemoteVideoMuted) {
174
+            if (!data ||
175
+                !("packetLoss" in data) ||
176
+                !("total" in data.packetLoss)) {
177
+                this.eventEmitter.emit(
178
+                    ConnectionQualityEvents.REMOTE_STATS_UPDATED,
179
+                    id,
180
+                    null,
181
+                    null);
182
+                return;
183
+            }
184
+
185
+            let inputResolution = data.resolution;
186
+            // Use only the fields we need
187
+            data = {bitrate: data.bitrate, packetLoss: data.packetLoss};
188
+
189
+            this.remoteStats[id] = data;
190
+
191
+            let val = this._getNewQualityValue(
192
+                data,
193
+                this.remoteConnectionQuality[id],
194
+                remoteVideoType,
195
+                isRemoteVideoMuted,
196
+                inputResolution);
197
+            if (val !== undefined)
198
+                this.remoteConnectionQuality[id] = val;
199
+
200
+            this.eventEmitter.emit(
201
+                ConnectionQualityEvents.REMOTE_STATS_UPDATED,
202
+                id,
203
+                this.remoteConnectionQuality[id],
204
+                this.remoteStats[id]);
205
+    }
206
+
207
+    /**
208
+     * Returns the local statistics.
209
+     */
210
+    getStats() {
211
+        return this.localStats;
212
+    }
213
+}

+ 0
- 7
service/connectionquality/CQEvents.js Parādīt failu

@@ -1,7 +0,0 @@
1
-var CQEvents = {
2
-    LOCALSTATS_UPDATED: "cq.localstats_updated",
3
-    REMOTESTATS_UPDATED: "cq.remotestats_updated",
4
-    STOP: "cq.stop"
5
-};
6
-
7
-module.exports = CQEvents;

+ 2
- 0
service/connectivity/ConnectionQualityEvents.js Parādīt failu

@@ -0,0 +1,2 @@
1
+export const LOCAL_STATS_UPDATED = "cq.local_stats_updated";
2
+export const REMOTE_STATS_UPDATED = "cq.remote_stats_updated";

Notiek ielāde…
Atcelt
Saglabāt