瀏覽代碼

Adds server-side speaker stats handling.

When the main domain advertises component with type 'speakerstats' every participant will send message to the component with the name of the room where this happen.
dev1
damencho 6 年之前
父節點
當前提交
6eb3157a84

+ 2
- 1
JitsiConferenceEventManager.js 查看文件

431
             }
431
             }
432
             if (conference.statistics && conference.myUserId() === id) {
432
             if (conference.statistics && conference.myUserId() === id) {
433
                 // We are the new dominant speaker.
433
                 // We are the new dominant speaker.
434
-                conference.statistics.sendDominantSpeakerEvent();
434
+                conference.statistics.sendDominantSpeakerEvent(
435
+                    conference.room.roomjid);
435
             }
436
             }
436
         });
437
         });
437
 
438
 

+ 2
- 2
modules/statistics/SpeakerStatsCollector.js 查看文件

52
     _onDominantSpeaker(dominantSpeakerId) {
52
     _onDominantSpeaker(dominantSpeakerId) {
53
         const oldDominantSpeaker
53
         const oldDominantSpeaker
54
             = this.stats.users[this.stats.dominantSpeakerId];
54
             = this.stats.users[this.stats.dominantSpeakerId];
55
-        const newDominantSpaker = this.stats.users[dominantSpeakerId];
55
+        const newDominantSpeaker = this.stats.users[dominantSpeakerId];
56
 
56
 
57
         oldDominantSpeaker && oldDominantSpeaker.setIsDominantSpeaker(false);
57
         oldDominantSpeaker && oldDominantSpeaker.setIsDominantSpeaker(false);
58
-        newDominantSpaker && newDominantSpaker.setIsDominantSpeaker(true);
58
+        newDominantSpeaker && newDominantSpeaker.setIsDominantSpeaker(true);
59
         this.stats.dominantSpeakerId = dominantSpeakerId;
59
         this.stats.dominantSpeakerId = dominantSpeakerId;
60
     }
60
     }
61
 
61
 

+ 5
- 1
modules/statistics/statistics.js 查看文件

498
 /**
498
 /**
499
  * Notifies the statistics module that we are now the dominant speaker of the
499
  * Notifies the statistics module that we are now the dominant speaker of the
500
  * conference.
500
  * conference.
501
+ * @param {String} roomJid - The room jid where the speaker event occurred.
501
  */
502
  */
502
-Statistics.prototype.sendDominantSpeakerEvent = function() {
503
+Statistics.prototype.sendDominantSpeakerEvent = function(roomJid) {
503
     for (const cs of this.callsStatsInstances.values()) {
504
     for (const cs of this.callsStatsInstances.values()) {
504
         cs.sendDominantSpeakerEvent();
505
         cs.sendDominantSpeakerEvent();
505
     }
506
     }
507
+
508
+    // xmpp send dominant speaker event
509
+    this.xmpp.sendDominantSpeakerEvent(roomJid);
506
 };
510
 };
507
 
511
 
508
 /**
512
 /**

+ 48
- 12
modules/xmpp/Caps.js 查看文件

119
         if (!user || !(user.version in this.versionToCapabilities)) {
119
         if (!user || !(user.version in this.versionToCapabilities)) {
120
             const node = user ? `${user.node}#${user.version}` : null;
120
             const node = user ? `${user.node}#${user.version}` : null;
121
 
121
 
122
-
123
-            return new Promise((resolve, reject) =>
124
-                this.disco.info(jid, node, response => {
125
-                    const features = new Set();
126
-
127
-                    $(response)
128
-                        .find('>query>feature')
129
-                        .each(
130
-                            (idx, el) => features.add(el.getAttribute('var')));
122
+            return this._getDiscoInfo(jid, node, timeout)
123
+                .then(({ features }) => {
131
                     if (user) {
124
                     if (user) {
132
                         // TODO: Maybe use the version + node + hash as keys?
125
                         // TODO: Maybe use the version + node + hash as keys?
133
                         this.versionToCapabilities[user.version] = features;
126
                         this.versionToCapabilities[user.version] = features;
134
                     }
127
                     }
135
-                    resolve(features);
136
-                }, reject, timeout)
137
-            );
128
+
129
+                    return features;
130
+                });
138
         }
131
         }
139
 
132
 
140
         return Promise.resolve(this.versionToCapabilities[user.version]);
133
         return Promise.resolve(this.versionToCapabilities[user.version]);
141
     }
134
     }
142
 
135
 
136
+    /**
137
+     * Returns a set with the features for a host.
138
+     * @param {String} jid the jid of the host
139
+     * @param {int} timeout the timeout in ms for reply from the host.
140
+     * @returns {Promise<Set<String>, Error>}
141
+     */
142
+    getFeaturesAndIdentities(jid, timeout = 5000) {
143
+        return this._getDiscoInfo(jid, null, timeout);
144
+    }
145
+
146
+    /**
147
+     * Returns a set with the features and identities for a host.
148
+     * @param {String} jid the jid of the host
149
+     * @param {String|null} node the node to query
150
+     * @param {int} timeout the timeout in ms for reply from the host.
151
+     * @returns {Promise<Object>}
152
+     * @private
153
+     */
154
+    _getDiscoInfo(jid, node, timeout) {
155
+        return new Promise((resolve, reject) =>
156
+            this.disco.info(jid, node, response => {
157
+                const features = new Set();
158
+                const identities = new Set();
159
+
160
+                $(response)
161
+                    .find('>query>feature')
162
+                    .each(
163
+                        (_, el) => features.add(el.getAttribute('var')));
164
+                $(response)
165
+                    .find('>query>identity')
166
+                    .each(
167
+                        (_, el) => identities.add({
168
+                            type: el.getAttribute('type'),
169
+                            name: el.getAttribute('name'),
170
+                            category: el.getAttribute('category')
171
+                        }));
172
+                resolve({
173
+                    features,
174
+                    identities });
175
+            }, reject, timeout)
176
+        );
177
+    }
178
+
143
     /**
179
     /**
144
      * Adds ChatRoom instance to the list of rooms. Adds listeners to the room
180
      * Adds ChatRoom instance to the list of rooms. Adds listeners to the room
145
      * and adds "c" element to the presences of the room.
181
      * and adds "c" element to the presences of the room.

+ 0
- 18
modules/xmpp/strophe.ping.js 查看文件

81
 
81
 
82
     /* eslint-enable max-params */
82
     /* eslint-enable max-params */
83
 
83
 
84
-    /**
85
-     * Checks if given <tt>jid</tt> has XEP-0199 ping support.
86
-     * @param jid the JID to be checked for ping support.
87
-     * @param callback function with boolean argument which will be
88
-     * <tt>true</tt> if XEP-0199 ping is supported by given <tt>jid</tt>
89
-     */
90
-    hasPingSupport(jid, callback) {
91
-        this.xmpp.caps.getFeatures(jid).then(features =>
92
-            callback(features.has('urn:xmpp:ping')), error => {
93
-            const errmsg = 'Ping feature discovery error';
94
-
95
-            GlobalOnErrorHandler.callErrorHandler(
96
-                new Error(`${errmsg}: ${error}`));
97
-            logger.error(errmsg, error);
98
-            callback(false);
99
-        });
100
-    }
101
-
102
     /**
84
     /**
103
      * Starts to send ping in given interval to specified remote JID.
85
      * Starts to send ping in given interval to specified remote JID.
104
      * This plugin supports only one such task and <tt>stopInterval</tt>
86
      * This plugin supports only one such task and <tt>stopInterval</tt>

+ 41
- 6
modules/xmpp/xmpp.js 查看文件

1
 /* global $ */
1
 /* global $ */
2
 
2
 
3
 import { getLogger } from 'jitsi-meet-logger';
3
 import { getLogger } from 'jitsi-meet-logger';
4
-import { Strophe } from 'strophe.js';
4
+import { $msg, Strophe } from 'strophe.js';
5
 import 'strophejs-plugin-disco';
5
 import 'strophejs-plugin-disco';
6
 
6
 
7
 import RandomUtil from '../util/RandomUtil';
7
 import RandomUtil from '../util/RandomUtil';
16
 import initStropheLogger from './strophe.logger';
16
 import initStropheLogger from './strophe.logger';
17
 import Listenable from '../util/Listenable';
17
 import Listenable from '../util/Listenable';
18
 import Caps from './Caps';
18
 import Caps from './Caps';
19
+import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
19
 
20
 
20
 const logger = getLogger(__filename);
21
 const logger = getLogger(__filename);
21
 
22
 
154
             // Schedule ping ?
155
             // Schedule ping ?
155
             const pingJid = this.connection.domain;
156
             const pingJid = this.connection.domain;
156
 
157
 
157
-            this.connection.ping.hasPingSupport(
158
-                pingJid,
159
-                hasPing => {
160
-                    if (hasPing) {
158
+            this.caps.getFeaturesAndIdentities(pingJid)
159
+                .then(({ features, identities }) => {
160
+                    if (features.has(Strophe.NS.PING)) {
161
                         this.connection.ping.startInterval(pingJid);
161
                         this.connection.ping.startInterval(pingJid);
162
                     } else {
162
                     } else {
163
                         logger.warn(`Ping NOT supported by ${pingJid}`);
163
                         logger.warn(`Ping NOT supported by ${pingJid}`);
164
                     }
164
                     }
165
+
166
+                    // check for speakerstats
167
+                    identities.forEach(identity => {
168
+                        if (identity.type === 'speakerstats') {
169
+                            this.speakerStatsComponentAddress = identity.name;
170
+                        }
171
+                    });
172
+                })
173
+                .catch(error => {
174
+                    const errmsg = 'Feature discovery error';
175
+
176
+                    GlobalOnErrorHandler.callErrorHandler(
177
+                        new Error(`${errmsg}: ${error}`));
178
+                    logger.error(errmsg, error);
165
                 });
179
                 });
166
 
180
 
167
             if (credentials.password) {
181
             if (credentials.password) {
168
                 this.authenticatedUser = true;
182
                 this.authenticatedUser = true;
169
             }
183
             }
170
             if (this.connection && this.connection.connected
184
             if (this.connection && this.connection.connected
171
-                    && Strophe.getResourceFromJid(this.connection.jid)) {
185
+                && Strophe.getResourceFromJid(this.connection.jid)) {
172
                 // .connected is true while connecting?
186
                 // .connected is true while connecting?
173
                 // this.connection.send($pres());
187
                 // this.connection.send($pres());
174
                 this.eventEmitter.emit(
188
                 this.eventEmitter.emit(
553
 
567
 
554
         return details;
568
         return details;
555
     }
569
     }
570
+
571
+    /**
572
+     * Notifies speaker stats component if available that we are the new
573
+     * dominant speaker in the conference.
574
+     * @param {String} roomJid - The room jid where the speaker event occurred.
575
+     */
576
+    sendDominantSpeakerEvent(roomJid) {
577
+        // no speaker stats component advertised
578
+        if (!this.speakerStatsComponentAddress || !roomJid) {
579
+            return;
580
+        }
581
+
582
+        const msg = $msg({ to: this.speakerStatsComponentAddress });
583
+
584
+        msg.c('speakerstats', {
585
+            xmlns: 'http://jitsi.org/jitmeet',
586
+            room: `${roomJid}` })
587
+            .up();
588
+
589
+        this.connection.send(msg);
590
+    }
556
 }
591
 }

Loading…
取消
儲存