Browse Source

Fixes issue with adding muted video stream to peerconnection.

dev1
hristoterezov 9 years ago
parent
commit
4220f6cde8

+ 1
- 0
modules/RTC/JitsiLocalTrack.js View File

@@ -26,6 +26,7 @@ function JitsiLocalTrack(stream, videoType,
26 26
                     JitsiTrackEvents.TRACK_STOPPED);
27 27
             this.dontFireRemoveEvent = false;
28 28
         }.bind(this));
29
+    this.initialMSID = this.getMSID();
29 30
 }
30 31
 
31 32
 JitsiLocalTrack.prototype = Object.create(JitsiTrack.prototype);

+ 30
- 2
modules/RTC/RTC.js View File

@@ -91,11 +91,39 @@ RTC.obtainAudioAndVideoPermissions = function (options) {
91 91
 
92 92
 RTC.prototype.onIncommingCall = function(event) {
93 93
     if(this.options.config.openSctp)
94
-        this.dataChannels = new DataChannels(event.peerconnection, this.eventEmitter);
94
+        this.dataChannels = new DataChannels(event.peerconnection,
95
+            this.eventEmitter);
95 96
     for(var i = 0; i < this.localStreams.length; i++)
96 97
         if(this.localStreams[i])
97 98
         {
98
-            this.room.addStream(this.localStreams[i].getOriginalStream(), function () {});
99
+            var ssrcInfo = null;
100
+            if(this.localStreams[i].isMuted() &&
101
+                this.localStreams[i].getType() === "video") {
102
+                /**
103
+                 * Handles issues when the stream is added before the peerconnection is created.
104
+                 * The peerconnection is created when second participant enters the call. In
105
+                 * that use case the track doesn't have information about it's ssrcs and no
106
+                 * jingle packets are sent. That can cause inconsistant behavior later.
107
+                 *
108
+                 * For example:
109
+                 * If we mute the stream and than second participant enter it's remote SDP won't
110
+                 * include that track. On unmute we are not sending any jingle packets which
111
+                 * will brake the unmute.
112
+                 *
113
+                 * In order to solve issues like the above one here we have to generate the ssrc
114
+                 * information for the track .
115
+                 */
116
+                this.localStreams[i]._setSSRC(
117
+                    this.room.generateNewStreamSSRCInfo());
118
+                ssrcInfo = {
119
+                    mtype: this.localStreams[i].getType(),
120
+                    type: "addMuted",
121
+                    ssrc: this.localStreams[i].ssrc,
122
+                    msid: this.localStreams[i].initialMSID
123
+                }
124
+            }
125
+            this.room.addStream(this.localStreams[i].getOriginalStream(),
126
+                function () {}, ssrcInfo);
99 127
         }
100 128
 }
101 129
 

+ 14
- 0
modules/xmpp/ChatRoom.js View File

@@ -581,6 +581,20 @@ ChatRoom.prototype.addStream = function (stream, callback, ssrcInfo) {
581 581
     }
582 582
 };
583 583
 
584
+/**
585
+ * Generate ssrc info object for a stream with the following properties:
586
+ * - ssrcs - Array of the ssrcs associated with the stream.
587
+ * - groups - Array of the groups associated with the stream.
588
+ */
589
+ChatRoom.prototype.generateNewStreamSSRCInfo = function () {
590
+    if(!this.session) {
591
+        logger.warn("The call haven't been started. " +
592
+            "Cannot generate ssrc info at the moment!");
593
+        return null;
594
+    }
595
+    return this.session.generateNewStreamSSRCInfo();
596
+};
597
+
584 598
 ChatRoom.prototype.setVideoMute = function (mute, callback, options) {
585 599
     var self = this;
586 600
     this.sendVideoInfoPresence(mute);

+ 74
- 20
modules/xmpp/JingleSessionPC.js View File

@@ -1038,7 +1038,9 @@ JingleSessionPC.prototype.addStream = function (stream, callback, ssrcInfo) {
1038 1038
         if(this.peerconnection.localDescription) {
1039 1039
             oldSdp = new SDP(this.peerconnection.localDescription.sdp);
1040 1040
         }
1041
-        if(stream)
1041
+        //when adding muted stream we have to pass the ssrcInfo but we don't
1042
+        //have a stream
1043
+        if(stream || ssrcInfo)
1042 1044
             this.peerconnection.addStream(stream, ssrcInfo);
1043 1045
     }
1044 1046
 
@@ -1054,7 +1056,8 @@ JingleSessionPC.prototype.addStream = function (stream, callback, ssrcInfo) {
1054 1056
         logger.log('modify sources done');
1055 1057
 
1056 1058
         callback();
1057
-        if(ssrcInfo) { //available only on video unmute
1059
+        if(ssrcInfo) {
1060
+            //available only on video unmute or when adding muted stream
1058 1061
             self.modifiedSSRCs[ssrcInfo.type] =
1059 1062
                 self.modifiedSSRCs[ssrcInfo.type] || [];
1060 1063
             self.modifiedSSRCs[ssrcInfo.type].push(ssrcInfo);
@@ -1065,6 +1068,15 @@ JingleSessionPC.prototype.addStream = function (stream, callback, ssrcInfo) {
1065 1068
     });
1066 1069
 }
1067 1070
 
1071
+/**
1072
+ * Generate ssrc info object for a stream with the following properties:
1073
+ * - ssrcs - Array of the ssrcs associated with the stream.
1074
+ * - groups - Array of the groups associated with the stream.
1075
+ */
1076
+JingleSessionPC.prototype.generateNewStreamSSRCInfo = function () {
1077
+    return this.peerconnection.generateNewStreamSSRCInfo();
1078
+};
1079
+
1068 1080
 /**
1069 1081
  * Remove streams.
1070 1082
  * @param stream stream that will be removed.
@@ -1420,6 +1432,38 @@ JingleSessionPC.prototype.fixSourceAddJingle = function (jingle) {
1420 1432
             });
1421 1433
         });
1422 1434
     }
1435
+
1436
+    ssrcs = this.modifiedSSRCs["addMuted"];
1437
+    this.modifiedSSRCs["addMuted"] = [];
1438
+    if(ssrcs && ssrcs.length) {
1439
+        ssrcs.forEach(function (ssrcObj) {
1440
+            var desc = createDescriptionNode(jingle, ssrcObj.mtype);
1441
+            var cname = Math.random().toString(36).substring(2);
1442
+            ssrcObj.ssrc.ssrcs.forEach(function (ssrc) {
1443
+                var sourceNode = desc.find(">source[ssrc=\"" +ssrc + "\"]");
1444
+                sourceNode.remove();
1445
+                var sourceXML = "<source " +
1446
+                    "xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\" ssrc=\"" +
1447
+                    ssrc + "\">" +
1448
+                    "<parameter xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"" +
1449
+                    " value=\"" + ssrcObj.msid + "\" name=\"msid\"/>" +
1450
+                    "<parameter xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"" +
1451
+                    " value=\"" + cname + "\" name=\"cname\" />" + "</source>";
1452
+                desc.append(sourceXML);
1453
+            });
1454
+            ssrcObj.ssrc.groups.forEach(function (group) {
1455
+                var groupNode = desc.find(">ssrc-group[semantics=\"" +
1456
+                    group.group.semantics + "\"]:has(source[ssrc=\"" + group.primarySSRC +
1457
+                    "\"])");
1458
+                groupNode.remove();
1459
+                desc.append("<ssrc-group semantics=\"" +
1460
+                    group.group.semantics +
1461
+                    "\" xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"><source ssrc=\"" +
1462
+                    group.group.ssrcs.split(" ").join("\"/><source ssrc=\"") + "\"/>" +
1463
+                    "</ssrc-group>");
1464
+            });
1465
+        });
1466
+    }
1423 1467
 }
1424 1468
 
1425 1469
 /**
@@ -1452,24 +1496,7 @@ JingleSessionPC.prototype.fixSourceRemoveJingle = function(jingle) {
1452 1496
     this.modifiedSSRCs["remove"] = [];
1453 1497
     if(ssrcs && ssrcs.length)
1454 1498
         ssrcs.forEach(function (ssrcObj) {
1455
-            var content = $(jingle.tree()).find(">jingle>content[name=\"" +
1456
-                ssrcObj.mtype + "\"]");
1457
-
1458
-            if(!content || !content.length) {
1459
-                $(jingle.tree()).find(">jingle").append(
1460
-                    "<content name=\"" + ssrcObj.mtype + "\"></content>");
1461
-                content = $(jingle.tree()).find(">jingle>content[name=\"" +
1462
-                    ssrcObj.mtype + "\"]");
1463
-            }
1464
-
1465
-            var desc = content.find(">description");
1466
-            if(!desc || !desc.length) {
1467
-                content.append("<description " +
1468
-                    "xmlns=\"urn:xmpp:jingle:apps:rtp:1\" media=\"" +
1469
-                    ssrcObj.mtype + "\"></description>");
1470
-                desc = content.find(">description");
1471
-            }
1472
-
1499
+            var desc = createDescriptionNode(jingle, ssrcObj.mtype);
1473 1500
             ssrcObj.ssrc.ssrcs.forEach(function (ssrc) {
1474 1501
                 var sourceNode = desc.find(">source[ssrc=\"" +ssrc + "\"]");
1475 1502
                 if(!sourceNode || !sourceNode.length) {
@@ -1494,4 +1521,31 @@ JingleSessionPC.prototype.fixSourceRemoveJingle = function(jingle) {
1494 1521
         });
1495 1522
 }
1496 1523
 
1524
+/**
1525
+ * Returns the description node related to the passed content type. If the node
1526
+ * doesn't exists it will be created.
1527
+ * @param jingle - the jingle packet
1528
+ * @param mtype - the content type(audio, video, etc.)
1529
+ */
1530
+function createDescriptionNode(jingle, mtype) {
1531
+    var content = $(jingle.tree()).find(">jingle>content[name=\"" +
1532
+        mtype + "\"]");
1533
+
1534
+    if(!content || !content.length) {
1535
+        $(jingle.tree()).find(">jingle").append(
1536
+            "<content name=\"" + mtype + "\"></content>");
1537
+        content = $(jingle.tree()).find(">jingle>content[name=\"" +
1538
+            mtype + "\"]");
1539
+    }
1540
+
1541
+    var desc = content.find(">description");
1542
+    if(!desc || !desc.length) {
1543
+        content.append("<description " +
1544
+            "xmlns=\"urn:xmpp:jingle:apps:rtp:1\" media=\"" +
1545
+            mtype + "\"></description>");
1546
+        desc = content.find(">description");
1547
+    }
1548
+    return desc;
1549
+}
1550
+
1497 1551
 module.exports = JingleSessionPC;

+ 34
- 6
modules/xmpp/TraceablePeerConnection.js View File

@@ -4,6 +4,9 @@ var logger = require("jitsi-meet-logger").getLogger(__filename);
4 4
 var RTCBrowserType = require("../RTC/RTCBrowserType.js");
5 5
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
6 6
 var transform = require('sdp-transform');
7
+var RandomUtil = require('../util/RandomUtil');
8
+
9
+var SIMULCAST_LAYERS = 3;
7 10
 
8 11
 function TraceablePeerConnection(ice_config, constraints, session) {
9 12
     var self = this;
@@ -29,7 +32,7 @@ function TraceablePeerConnection(ice_config, constraints, session) {
29 32
     var Interop = require('sdp-interop').Interop;
30 33
     this.interop = new Interop();
31 34
     var Simulcast = require('sdp-simulcast');
32
-    this.simulcast = new Simulcast({numOfLayers: 3,
35
+    this.simulcast = new Simulcast({numOfLayers: SIMULCAST_LAYERS,
33 36
         explodeRemoteSimulcast: false});
34 37
     this.eventEmitter = this.session.room.eventEmitter;
35 38
 
@@ -156,8 +159,6 @@ TraceablePeerConnection.prototype.ssrcReplacement = function (desc) {
156 159
         return desc;
157 160
     }
158 161
 
159
-    var RandomUtil = require('../util/RandomUtil');
160
-
161 162
     var session = transform.parse(desc.sdp);
162 163
     if (!Array.isArray(session.media))
163 164
     {
@@ -180,6 +181,7 @@ TraceablePeerConnection.prototype.ssrcReplacement = function (desc) {
180 181
             var ssrcOperation = SSRCs[0];
181 182
             switch(ssrcOperation.type) {
182 183
                 case "mute":
184
+                case "addMuted":
183 185
                 //FIXME: If we want to support multiple streams we have to add
184 186
                 // recv-only ssrcs for the
185 187
                 // muted streams on every change until the stream is unmuted
@@ -202,7 +204,12 @@ TraceablePeerConnection.prototype.ssrcReplacement = function (desc) {
202 204
                         id: ssrc,
203 205
                         attribute: 'cname',
204 206
                         value: ['recvonly-', ssrc].join('')
205
-                    })
207
+                    });
208
+                    // If this is executed for another reason we are going to
209
+                    // include that ssrc as receive only again instead of
210
+                    // generating new one. Here we are assuming that we have
211
+                    // only 1 video stream that is muted.
212
+                    this.recvOnlySSRCs[bLine.type] = ssrc;
206 213
                     break;
207 214
                 case "unmute":
208 215
                     if(!ssrcOperation.ssrc || !ssrcOperation.ssrc.ssrcs ||
@@ -451,10 +458,11 @@ Object.keys(getters).forEach(function (prop) {
451 458
 });
452 459
 
453 460
 TraceablePeerConnection.prototype.addStream = function (stream, ssrcInfo) {
454
-    this.trace('addStream', stream.id);
461
+    this.trace('addStream', stream? stream.id : "null");
455 462
     try
456 463
     {
457
-        this.peerconnection.addStream(stream);
464
+        if(stream)
465
+            this.peerconnection.addStream(stream);
458 466
         if(ssrcInfo && this.replaceSSRCs[ssrcInfo.mtype])
459 467
             this.replaceSSRCs[ssrcInfo.mtype].push(ssrcInfo);
460 468
     }
@@ -633,4 +641,24 @@ TraceablePeerConnection.prototype.getStats = function(callback, errback) {
633 641
     }
634 642
 };
635 643
 
644
+/**
645
+ * Generate ssrc info object for a stream with the following properties:
646
+ * - ssrcs - Array of the ssrcs associated with the stream.
647
+ * - groups - Array of the groups associated with the stream.
648
+ */
649
+TraceablePeerConnection.prototype.generateNewStreamSSRCInfo = function () {
650
+    if (!this.session.room.options.disableSimulcast
651
+        && this.simulcast.isSupported()) {
652
+        var ssrcInfo = {ssrcs: [], groups: []};
653
+        for(var i = 0; i < SIMULCAST_LAYERS; i++)
654
+            ssrcInfo.ssrcs.push(RandomUtil.randomInt(1, 0xffffffff));
655
+        ssrcInfo.groups.push({
656
+            primarySSRC: ssrcInfo.ssrcs[0],
657
+            group: {ssrcs: ssrcInfo.ssrcs.join(" "), semantics: "SIM"}});
658
+        return ssrcInfo;
659
+    } else {
660
+        return {ssrcs: [RandomUtil.randomInt(1, 0xffffffff)], groups: []};
661
+    }
662
+};
663
+
636 664
 module.exports = TraceablePeerConnection;

Loading…
Cancel
Save