Browse Source

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

master
George Politis 9 years ago
parent
commit
dc07fd733f

+ 2
- 1
modules/util/RandomUtil.js View File

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

+ 0
- 19
modules/xmpp/JingleSessionPC.js View File

@@ -10,7 +10,6 @@ var transform = require("sdp-transform");
10 10
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
11 11
 var RTCEvents = require("../../service/RTC/RTCEvents");
12 12
 var RTCBrowserType = require("../RTC/RTCBrowserType");
13
-var SSRCReplacement = require("./LocalSSRCReplacement");
14 13
 
15 14
 // Jingle stuff
16 15
 function JingleSessionPC(me, sid, connection, service, eventEmitter) {
@@ -272,8 +271,6 @@ JingleSessionPC.prototype.accept = function () {
272 271
             //console.log('setLocalDescription success');
273 272
             self.setLocalDescription();
274 273
 
275
-            SSRCReplacement.processSessionInit(accept);
276
-
277 274
             self.connection.sendIQ(accept,
278 275
                 function () {
279 276
                     var ack = {};
@@ -371,8 +368,6 @@ JingleSessionPC.prototype.sendIceCandidate = function (candidate) {
371 368
                     self.initiator == self.me ? 'initiator' : 'responder',
372 369
                     ssrc);
373 370
 
374
-                SSRCReplacement.processSessionInit(init);
375
-
376 371
                 self.connection.sendIQ(init,
377 372
                     function () {
378 373
                         //console.log('session initiate ack');
@@ -489,8 +484,6 @@ JingleSessionPC.prototype.createdOffer = function (sdp) {
489 484
             init,
490 485
             this.initiator == this.me ? 'initiator' : 'responder');
491 486
 
492
-        SSRCReplacement.processSessionInit(init);
493
-
494 487
         self.connection.sendIQ(init,
495 488
             function () {
496 489
                 var ack = {};
@@ -787,8 +780,6 @@ JingleSessionPC.prototype.createdAnswer = function (sdp, provisional) {
787 780
                     self.initiator == self.me ? 'initiator' : 'responder',
788 781
                     ssrcs);
789 782
 
790
-                SSRCReplacement.processSessionInit(accept);
791
-
792 783
                 self.connection.sendIQ(accept,
793 784
                     function () {
794 785
                         var ack = {};
@@ -1227,11 +1218,6 @@ JingleSessionPC.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
1227 1218
     );
1228 1219
     var removed = sdpDiffer.toJingle(remove);
1229 1220
 
1230
-    // Let 'source-remove' IQ through the hack and see if we're allowed to send
1231
-    // it in the current form
1232
-    if (removed)
1233
-        remove = SSRCReplacement.processSourceRemove(remove);
1234
-
1235 1221
     if (removed && remove) {
1236 1222
         console.info("Sending source-remove", remove);
1237 1223
         this.connection.sendIQ(remove,
@@ -1258,11 +1244,6 @@ JingleSessionPC.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
1258 1244
     );
1259 1245
     var added = sdpDiffer.toJingle(add);
1260 1246
 
1261
-    // Let 'source-add' IQ through the hack and see if we're allowed to send
1262
-    // it in the current form
1263
-    if (added)
1264
-        add = SSRCReplacement.processSourceAdd(add);
1265
-
1266 1247
     if (added && add) {
1267 1248
         console.info("Sending source-add", add);
1268 1249
         this.connection.sendIQ(add,

+ 0
- 271
modules/xmpp/LocalSSRCReplacement.js View File

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

+ 51
- 10
modules/xmpp/TraceablePeerConnection.js View File

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

Loading…
Cancel
Save