浏览代码

Removes the local SSRC replacement hack and fixes video muting/unmuting when simulcast is enabled.

Conflicts:
	modules/xmpp/JingleSessionPC.js
	modules/xmpp/LocalSSRCReplacement.js
	modules/xmpp/TraceablePeerConnection.js
dev1
George Politis 9 年前
父节点
当前提交
2c317e3b17

+ 2
- 1
modules/util/RandomUtil.js 查看文件

@@ -67,7 +67,8 @@ var RandomUtil = {
67 67
         return ret;
68 68
     },
69 69
     randomElement: randomElement,
70
-    randomAlphanumStr: randomAlphanumStr
70
+    randomAlphanumStr: randomAlphanumStr,
71
+    randomInt: randomInt
71 72
 };
72 73
 
73 74
 module.exports = RandomUtil;

+ 0
- 19
modules/xmpp/JingleSessionPC.js 查看文件

@@ -10,7 +10,6 @@ var async = require("async");
10 10
 var transform = require("sdp-transform");
11 11
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
12 12
 var RTCBrowserType = require("../RTC/RTCBrowserType");
13
-var SSRCReplacement = require("./LocalSSRCReplacement");
14 13
 var RTC = require("../RTC/RTC");
15 14
 
16 15
 // Jingle stuff
@@ -274,8 +273,6 @@ JingleSessionPC.prototype.accept = function () {
274 273
             //logger.log('setLocalDescription success');
275 274
             self.setLocalDescription();
276 275
 
277
-            SSRCReplacement.processSessionInit(accept);
278
-
279 276
             self.connection.sendIQ(accept,
280 277
                 function () {
281 278
                     var ack = {};
@@ -373,8 +370,6 @@ JingleSessionPC.prototype.sendIceCandidate = function (candidate) {
373 370
                     self.initiator == self.me ? 'initiator' : 'responder',
374 371
                     ssrc);
375 372
 
376
-                SSRCReplacement.processSessionInit(init);
377
-
378 373
                 self.connection.sendIQ(init,
379 374
                     function () {
380 375
                         //logger.log('session initiate ack');
@@ -491,8 +486,6 @@ JingleSessionPC.prototype.createdOffer = function (sdp) {
491 486
             init,
492 487
             this.initiator == this.me ? 'initiator' : 'responder');
493 488
 
494
-        SSRCReplacement.processSessionInit(init);
495
-
496 489
         self.connection.sendIQ(init,
497 490
             function () {
498 491
                 var ack = {};
@@ -793,8 +786,6 @@ JingleSessionPC.prototype.createdAnswer = function (sdp, provisional) {
793 786
                     self.initiator == self.me ? 'initiator' : 'responder',
794 787
                     ssrcs);
795 788
 
796
-                SSRCReplacement.processSessionInit(accept);
797
-
798 789
                 self.connection.sendIQ(accept,
799 790
                     function () {
800 791
                         var ack = {};
@@ -1349,11 +1340,6 @@ JingleSessionPC.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
1349 1340
     );
1350 1341
     var removed = sdpDiffer.toJingle(remove);
1351 1342
 
1352
-    // Let 'source-remove' IQ through the hack and see if we're allowed to send
1353
-    // it in the current form
1354
-    if (removed)
1355
-        remove = SSRCReplacement.processSourceRemove(remove);
1356
-
1357 1343
     if (removed && remove) {
1358 1344
         logger.info("Sending source-remove", remove);
1359 1345
         this.connection.sendIQ(remove,
@@ -1380,11 +1366,6 @@ JingleSessionPC.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
1380 1366
     );
1381 1367
     var added = sdpDiffer.toJingle(add);
1382 1368
 
1383
-    // Let 'source-add' IQ through the hack and see if we're allowed to send
1384
-    // it in the current form
1385
-    if (added)
1386
-        add = SSRCReplacement.processSourceAdd(add);
1387
-
1388 1369
     if (added && add) {
1389 1370
         logger.info("Sending source-add", add);
1390 1371
         this.connection.sendIQ(add,

+ 0
- 292
modules/xmpp/LocalSSRCReplacement.js 查看文件

@@ -1,292 +0,0 @@
1
-/* global $ */
2
-var logger = require("jitsi-meet-logger").getLogger(__filename);
3
-
4
-
5
-/*
6
- Here we do modifications of local video SSRCs. There are 2 situations we have
7
- to handle:
8
-
9
- 1. We generate SSRC for local recvonly video stream. This is the case when we
10
-    have no local camera and it is not generated automatically, but SSRC=1 is
11
-    used implicitly. If that happens RTCP packets will be dropped by the JVB
12
-    and we won't be able to request video key frames correctly.
13
-
14
- 2. A hack to re-use SSRC of the first video stream for any new stream created
15
-    in future. It turned out that Chrome may keep on using the SSRC of removed
16
-    video stream in RTCP even though a new one has been created. So we just
17
-    want to avoid that by re-using it. Jingle 'source-remove'/'source-add'
18
-    notifications are blocked once first video SSRC is advertised to the focus.
19
-
20
- What this hack does:
21
-
22
- 1. Stores the SSRC of the first video stream created by
23
-   a) scanning Jingle session-accept/session-invite for existing video SSRC
24
-   b) watching for 'source-add' for new video stream if it has not been
25
-      created in step a)
26
- 2. Exposes method 'mungeLocalVideoSSRC' which replaces any new video SSRC with
27
-    the stored one. It is called by 'TracablePeerConnection' before local SDP is
28
-    returned to the other parts of the application.
29
- 3. Scans 'source-remove'/'source-add' notifications for stored video SSRC and
30
-    blocks those notifications. This makes Jicofo and all participants think
31
-    that it exists all the time even if the video stream has been removed or
32
-    replaced locally. Thanks to that there is no additional signaling activity
33
-    on video mute or when switching to the desktop stream.
34
- */
35
-
36
-var SDP = require('./SDP');
37
-var RandomUtil = require('../util/RandomUtil');
38
-var RTCBrowserType = require('../RTC/RTCBrowserType');
39
-
40
-/**
41
- * The hack is enabled on all browsers except FF by default
42
- * FIXME finish the hack once removeStream method is implemented in FF
43
- * @type {boolean}
44
- */
45
-var isEnabled = !RTCBrowserType.isFirefox();
46
-
47
-
48
-/**
49
- * Stored SSRC of local video stream.
50
- */
51
-var localVideoSSRC;
52
-
53
-/**
54
- * SSRC used for recvonly video stream when we have no local camera.
55
- * This is in order to tell Chrome what SSRC should be used in RTCP requests
56
- * instead of 1.
57
- */
58
-var localRecvOnlySSRC, localRecvOnlyMSID, localRecvOnlyMSLabel, localRecvOnlyLabel;
59
-
60
-/**
61
- * cname for <tt>localRecvOnlySSRC</tt>
62
- */
63
-var localRecvOnlyCName;
64
-
65
-/**
66
- * Method removes <source> element which describes <tt>localVideoSSRC</tt>
67
- * from given Jingle IQ.
68
- * @param modifyIq 'source-add' or 'source-remove' Jingle IQ.
69
- * @param actionName display name of the action which will be printed in log
70
- *        messages.
71
- * @returns {*} modified Jingle IQ, so that it does not contain <source> element
72
- *          corresponding to <tt>localVideoSSRC</tt> or <tt>null</tt> if no
73
- *          other SSRCs left to be signaled after removing it.
74
- */
75
-var filterOutSource = function (modifyIq, actionName) {
76
-    var modifyIqTree = $(modifyIq.tree());
77
-
78
-    if (!localVideoSSRC)
79
-        return modifyIqTree[0];
80
-
81
-    var videoSSRC = modifyIqTree.find(
82
-        '>jingle>content[name="video"]' +
83
-        '>description>source[ssrc="' + localVideoSSRC + '"]');
84
-
85
-    if (!videoSSRC.length) {
86
-        return modifyIqTree[0];
87
-    }
88
-
89
-    logger.info(
90
-        'Blocking ' + actionName + ' for local video SSRC: ' + localVideoSSRC);
91
-
92
-    videoSSRC.remove();
93
-
94
-    // Check if any sources still left to be added/removed
95
-    if (modifyIqTree.find('>jingle>content>description>source').length) {
96
-        return modifyIqTree[0];
97
-    } else {
98
-        return null;
99
-    }
100
-};
101
-
102
-/**
103
- * Scans given Jingle IQ for video SSRC and stores it.
104
- * @param jingleIq the Jingle IQ to be scanned for video SSRC.
105
- */
106
-var storeLocalVideoSSRC = function (jingleIq) {
107
-    var videoSSRCs =
108
-        $(jingleIq.tree())
109
-            .find('>jingle>content[name="video"]>description>source');
110
-
111
-    videoSSRCs.each(function (idx, ssrcElem) {
112
-        if (localVideoSSRC)
113
-            return;
114
-        // We consider SSRC real only if it has msid attribute
115
-        // recvonly streams in FF do not have it as well as local SSRCs
116
-        // we generate for recvonly streams in Chrome
117
-        var ssrSel = $(ssrcElem);
118
-        var msid = ssrSel.find('>parameter[name="msid"]');
119
-        if (msid.length) {
120
-            var ssrcVal = ssrSel.attr('ssrc');
121
-            if (ssrcVal) {
122
-                localVideoSSRC = ssrcVal;
123
-                logger.info('Stored local video SSRC' +
124
-                             ' for future re-use: ' + localVideoSSRC);
125
-            }
126
-        }
127
-    });
128
-};
129
-
130
-/**
131
- * Generates new label/mslabel attribute
132
- * @returns {string} label/mslabel attribute
133
- */
134
-function generateLabel() {
135
-    return RandomUtil.randomHexString(8) + "-" + RandomUtil.randomHexString(4) +
136
-        "-" + RandomUtil.randomHexString(4) + "-" +
137
-        RandomUtil.randomHexString(4) + "-" + RandomUtil.randomHexString(12);
138
-}
139
-
140
-/**
141
- * Generates new SSRC for local video recvonly stream.
142
- * FIXME what about eventual SSRC collision ?
143
- */
144
-function generateRecvonlySSRC() {
145
-
146
-    localVideoSSRC = localRecvOnlySSRC =
147
-        localVideoSSRC ?
148
-            localVideoSSRC : Math.random().toString(10).substring(2, 11);
149
-
150
-    localRecvOnlyCName =
151
-        Math.random().toString(36).substring(2);
152
-
153
-    localRecvOnlyMSLabel = generateLabel();
154
-    localRecvOnlyLabel = generateLabel();
155
-    localRecvOnlyMSID = localRecvOnlyMSLabel + " " + localRecvOnlyLabel;
156
-
157
-    logger.info(
158
-        "Generated local recvonly SSRC: " + localRecvOnlySSRC +
159
-        ", cname: " + localRecvOnlyCName);
160
-}
161
-
162
-var LocalSSRCReplacement = {
163
-    /**
164
-     * Method must be called before 'session-initiate' or 'session-invite' is
165
-     * sent. Scans the IQ for local video SSRC and stores it if detected.
166
-     *
167
-     * @param sessionInit our 'session-initiate' or 'session-accept' Jingle IQ
168
-     *        which will be scanned for local video SSRC.
169
-     */
170
-    processSessionInit: function (sessionInit) {
171
-        if (!isEnabled)
172
-            return;
173
-
174
-        if (localVideoSSRC) {
175
-            logger.error("Local SSRC stored already: " + localVideoSSRC);
176
-            return;
177
-        }
178
-        storeLocalVideoSSRC(sessionInit);
179
-    },
180
-    /**
181
-     * If we have local video SSRC stored searched given
182
-     * <tt>localDescription</tt> for video SSRC and makes sure it is replaced
183
-     * with the stored one.
184
-     * @param localDescription local description object that will have local
185
-     *        video SSRC replaced with the stored one
186
-     * @returns modified <tt>localDescription</tt> object.
187
-     */
188
-    mungeLocalVideoSSRC: function (localDescription) {
189
-        if (!isEnabled)
190
-            return localDescription;
191
-
192
-        if (!localDescription) {
193
-            logger.warn("localDescription is null or undefined");
194
-            return localDescription;
195
-        }
196
-
197
-        // IF we have local video SSRC stored make sure it is replaced
198
-        // with old SSRC
199
-        var sdp = new SDP(localDescription.sdp);
200
-        if (sdp.media.length < 2)
201
-            return;
202
-
203
-        if (localVideoSSRC && sdp.media[1].indexOf("a=ssrc:") !== -1 &&
204
-            !sdp.containsSSRC(localVideoSSRC)) {
205
-            // Get new video SSRC
206
-            var map = sdp.getMediaSsrcMap();
207
-            var videoPart = map[1];
208
-            var videoSSRCs = videoPart.ssrcs;
209
-            var newSSRC = Object.keys(videoSSRCs)[0];
210
-
211
-            logger.info(
212
-                "Replacing new video SSRC: " + newSSRC +
213
-                " with " + localVideoSSRC);
214
-
215
-            localDescription.sdp =
216
-                sdp.raw.replace(
217
-                    new RegExp('a=ssrc:' + newSSRC, 'g'),
218
-                    'a=ssrc:' + localVideoSSRC);
219
-        }
220
-        else if (sdp.media[1].indexOf('a=ssrc:') === -1 &&
221
-                 sdp.media[1].indexOf('a=recvonly') !== -1) {
222
-            // Make sure we have any SSRC for recvonly video stream
223
-            if (!localRecvOnlySSRC) {
224
-                generateRecvonlySSRC();
225
-            }
226
-
227
-            logger.info('No SSRC in video recvonly stream' +
228
-                         ' - adding SSRC: ' + localRecvOnlySSRC);
229
-
230
-            sdp.media[1] += 'a=ssrc:' + localRecvOnlySSRC +
231
-                            ' cname:' + localRecvOnlyCName + '\r\n' +
232
-                            'a=ssrc:' + localRecvOnlySSRC +
233
-                            ' msid:' + localRecvOnlyMSID + '\r\n' +
234
-                            'a=ssrc:' + localRecvOnlySSRC +
235
-                            ' mslabel:' + localRecvOnlyMSLabel + '\r\n' +
236
-                            'a=ssrc:' + localRecvOnlySSRC +
237
-                            ' label:' + localRecvOnlyLabel + '\r\n';
238
-
239
-            localDescription.sdp = sdp.session + sdp.media.join('');
240
-        }
241
-        return localDescription;
242
-    },
243
-    /**
244
-     * Method must be called before 'source-add' notification is sent. In case
245
-     * we have local video SSRC advertised already it will be removed from the
246
-     * notification. If no other SSRCs are described by given IQ null will be
247
-     * returned which means that there is no point in sending the notification.
248
-     * @param sourceAdd 'source-add' Jingle IQ to be processed
249
-     * @returns modified 'source-add' IQ which can be sent to the focus or
250
-     *          <tt>null</tt> if no notification shall be sent. It is no longer
251
-     *          a Strophe IQ Builder instance, but DOM element tree.
252
-     */
253
-    processSourceAdd: function (sourceAdd) {
254
-        if (!isEnabled)
255
-            return sourceAdd;
256
-
257
-        if (!localVideoSSRC) {
258
-            // Store local SSRC if available
259
-            storeLocalVideoSSRC(sourceAdd);
260
-            return sourceAdd;
261
-        } else {
262
-            return filterOutSource(sourceAdd, 'source-add');
263
-        }
264
-    },
265
-    /**
266
-     * Method must be called before 'source-remove' notification is sent.
267
-     * Removes local video SSRC from the notification. If there are no other
268
-     * SSRCs described in the given IQ <tt>null</tt> will be returned which
269
-     * means that there is no point in sending the notification.
270
-     * @param sourceRemove 'source-remove' Jingle IQ to be processed
271
-     * @returns modified 'source-remove' IQ which can be sent to the focus or
272
-     *          <tt>null</tt> if no notification shall be sent. It is no longer
273
-     *          a Strophe IQ Builder instance, but DOM element tree.
274
-     */
275
-    processSourceRemove: function (sourceRemove) {
276
-        if (!isEnabled)
277
-            return sourceRemove;
278
-
279
-        return filterOutSource(sourceRemove, 'source-remove');
280
-    },
281
-
282
-    /**
283
-     * Turns the hack on or off
284
-     * @param enabled <tt>true</tt> to enable the hack or <tt>false</tt>
285
-     *                to disable it
286
-     */
287
-    setEnabled: function (enabled) {
288
-        isEnabled = enabled;
289
-    }
290
-};
291
-
292
-module.exports = LocalSSRCReplacement;

+ 51
- 9
modules/xmpp/TraceablePeerConnection.js 查看文件

@@ -1,10 +1,8 @@
1 1
 /* global $ */
2 2
 var RTC = require('../RTC/RTC');
3
-
4 3
 var logger = require("jitsi-meet-logger").getLogger(__filename);
5 4
 var RTCBrowserType = require("../RTC/RTCBrowserType.js");
6 5
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
7
-var SSRCReplacement = require("./LocalSSRCReplacement");
8 6
 
9 7
 function TraceablePeerConnection(ice_config, constraints, session) {
10 8
     var self = this;
@@ -138,6 +136,47 @@ var dumpSDP = function(description) {
138 136
     return 'type: ' + description.type + '\r\n' + description.sdp;
139 137
 };
140 138
 
139
+var insertRecvOnlySSRC = function (desc) {
140
+    if (typeof desc !== 'object' || desc === null ||
141
+        typeof desc.sdp !== 'string') {
142
+        console.warn('An empty description was passed as an argument.');
143
+        return desc;
144
+    }
145
+
146
+    var transform = require('sdp-transform');
147
+    var RandomUtil = require('../util/RandomUtil');
148
+
149
+    var session = transform.parse(desc.sdp);
150
+    if (!Array.isArray(session.media))
151
+    {
152
+        return;
153
+    }
154
+
155
+    var modded = false;
156
+    session.media.forEach(function (bLine) {
157
+        if (bLine.direction != 'recvonly')
158
+        {
159
+            return;
160
+        }
161
+
162
+        modded = true;
163
+        if (!Array.isArray(bLine.ssrcs) || bLine.ssrcs.length === 0)
164
+        {
165
+            var ssrc = RandomUtil.randomInt(1, 0xffffffff);
166
+            bLine.ssrcs = [{
167
+                id: ssrc,
168
+                attribute: 'cname',
169
+                value: ['recvonly-', ssrc].join(' ')
170
+            }];
171
+        }
172
+    });
173
+
174
+    return (!modded) ? desc : new RTCSessionDescription({
175
+        type: desc.type,
176
+        sdp: transform.write(session),
177
+    });
178
+};
179
+
141 180
 /**
142 181
  * Takes a SessionDescription object and returns a "normalized" version.
143 182
  * Currently it only takes care of ordering the a=ssrc lines.
@@ -216,10 +255,6 @@ var getters = {
216 255
     localDescription:  function() {
217 256
         var desc = this.peerconnection.localDescription;
218 257
 
219
-            // FIXME this should probably be after the Unified Plan -> Plan B
220
-            // transformation.
221
-        desc = SSRCReplacement.mungeLocalVideoSSRC(desc);
222
-
223 258
         this.trace('getLocalDescription::preTransform', dumpSDP(desc));
224 259
 
225 260
         // if we're running on FF, transform to Plan B first.
@@ -370,7 +405,11 @@ TraceablePeerConnection.prototype.createOffer
370 405
                 self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer));
371 406
             }
372 407
 
373
-            offer = SSRCReplacement.mungeLocalVideoSSRC(offer);
408
+            if (RTCBrowserType.isChrome())
409
+            {
410
+                offer = insertRecvOnlySSRC(offer);
411
+                self.trace('createOfferOnSuccess::mungeLocalVideoSSRC', dumpSDP(offer));
412
+            }
374 413
 
375 414
             if (self.session.room.options.enableSimulcast && self.simulcast.isSupported()) {
376 415
                 offer = self.simulcast.mungeLocalDescription(offer);
@@ -399,8 +438,11 @@ TraceablePeerConnection.prototype.createAnswer
399 438
                 self.trace('createAnswerOnSuccess::postTransform (Plan B)', dumpSDP(answer));
400 439
             }
401 440
 
402
-            // munge local video SSRC
403
-            answer = SSRCReplacement.mungeLocalVideoSSRC(answer);
441
+            if (RTCBrowserType.isChrome())
442
+            {
443
+                answer = insertRecvOnlySSRC(answer);
444
+                self.trace('createAnswerOnSuccess::mungeLocalVideoSSRC', dumpSDP(answer));
445
+            }
404 446
 
405 447
             if (self.session.room.options.enableSimulcast && self.simulcast.isSupported()) {
406 448
                 answer = self.simulcast.mungeLocalDescription(answer);

正在加载...
取消
保存