Переглянути джерело

fix(TPC): Do not remove ssrcs from remote desc for p2p.

In unified plan, re-use of m-line (i.e., adding an SSRC, removing it and then adding it back) causes the browser to not render the media on Chrome and Safari. The WebRTC spec is not clear as to how browsers have to behave, this doesn't cause any issues on Firefox. As a workaround, only change the media direction and leave the ssrc in the remote desc. This automatically triggers a 'removetrack' event on the associated MediaStream and the track can be removed from the UI.
tags/v0.0.2
Jaya Allamsetty 4 роки тому
джерело
коміт
b43a9fa0ee

+ 19
- 0
modules/RTC/MockClasses.js Переглянути файл

@@ -4,6 +4,18 @@
4 4
  * Mock {@link TraceablePeerConnection} - add things as needed, but only things useful for all tests.
5 5
  */
6 6
 export class MockPeerConnection {
7
+
8
+    /**
9
+     * Constructor.
10
+     *
11
+     * @param {string} id RTC id
12
+     * @param {boolean} usesUnifiedPlan
13
+     */
14
+    constructor(id, usesUnifiedPlan) {
15
+        this.id = id;
16
+        this._usesUnifiedPlan = usesUnifiedPlan;
17
+    }
18
+
7 19
     /**
8 20
      * {@link TraceablePeerConnection.localDescription}.
9 21
      *
@@ -65,6 +77,13 @@ export class MockPeerConnection {
65 77
     setVideoTransferActive() {
66 78
         return false;
67 79
     }
80
+
81
+    /**
82
+     * {@link TraceablePeerConnection.usesUnifiedPlan}.
83
+     */
84
+    usesUnifiedPlan() {
85
+        return this._usesUnifiedPlan;
86
+    }
68 87
 }
69 88
 
70 89
 /**

+ 11
- 2
modules/RTC/TraceablePeerConnection.js Переглянути файл

@@ -1972,8 +1972,8 @@ TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {
1972 1972
 
1973 1973
         return this.tpcUtils.replaceTrack(oldTrack, newTrack)
1974 1974
 
1975
-            // renegotiate when SDP is used for simulcast munging
1976
-            .then(() => this.isSimulcastOn() && browser.usesSdpMungingForSimulcast());
1975
+            // Renegotiate when SDP is used for simulcast munging or when in p2p mode.
1976
+            .then(() => (this.isSimulcastOn() && browser.usesSdpMungingForSimulcast()) || this.isP2P);
1977 1977
     }
1978 1978
 
1979 1979
     logger.debug(`${this} TPC.replaceTrack using plan B`);
@@ -3043,6 +3043,15 @@ TraceablePeerConnection.prototype.generateNewStreamSSRCInfo = function(track) {
3043 3043
     return ssrcInfo;
3044 3044
 };
3045 3045
 
3046
+/**
3047
+ * Returns if the peer connection uses Unified plan implementation.
3048
+ *
3049
+ * @returns {boolean} True if the pc uses Unified plan, false otherwise.
3050
+ */
3051
+TraceablePeerConnection.prototype.usesUnifiedPlan = function() {
3052
+    return this._usesUnifiedPlan;
3053
+};
3054
+
3046 3055
 /**
3047 3056
  * Creates a text representation of this <tt>TraceablePeerConnection</tt>
3048 3057
  * instance.

+ 10
- 8
modules/sdp/LocalSdpMunger.js Переглянути файл

@@ -218,21 +218,23 @@ export default class LocalSdpMunger {
218 218
             }
219 219
         }
220 220
 
221
+        // Additional transformations related to MSID are applicable to Unified-plan implementation only.
222
+        if (!this.tpc.usesUnifiedPlan()) {
223
+            return;
224
+        }
225
+
221 226
         // If the msid attribute is missing, then remove the ssrc from the transformed description so that a
222 227
         // source-remove is signaled to Jicofo. This happens when the direction of the transceiver (or m-line)
223 228
         // is set to 'inactive' or 'recvonly' on Firefox, Chrome (unified) and Safari.
224
-        const msid = mediaSection.ssrcs.find(s => s.attribute === 'msid');
229
+        const mediaDirection = mediaSection.mLine?.direction;
225 230
 
226
-        if (!this.tpc.isP2P
227
-            && (!msid
228
-                || mediaSection.mLine?.direction === MediaDirection.RECVONLY
229
-                || mediaSection.mLine?.direction === MediaDirection.INACTIVE)) {
231
+        if (mediaDirection === MediaDirection.RECVONLY || mediaDirection === MediaDirection.INACTIVE) {
230 232
             mediaSection.ssrcs = undefined;
231 233
             mediaSection.ssrcGroups = undefined;
232 234
 
233
-        // Add the msid attribute if it is missing for p2p sources. Firefox doesn't produce a a=ssrc line
234
-        // with msid attribute.
235
-        } else if (this.tpc.isP2P && mediaSection.mLine?.direction === MediaDirection.SENDRECV) {
235
+        // Add the msid attribute if it is missing when the direction is sendrecv/sendonly. Firefox doesn't produce a
236
+        // a=ssrc line with msid attribute for p2p connection.
237
+        } else {
236 238
             const msidLine = mediaSection.mLine?.msid;
237 239
             const trackId = msidLine && msidLine.split(' ')[1];
238 240
             const sources = [ ...new Set(mediaSection.mLine?.ssrcs?.map(s => s.id)) ];

+ 36
- 3
modules/sdp/LocalSdpMunger.spec.js Переглянути файл

@@ -1,6 +1,8 @@
1 1
 
2 2
 import * as transform from 'sdp-transform';
3 3
 
4
+import { MockPeerConnection } from '../RTC/MockClasses';
5
+
4 6
 import LocalSdpMunger from './LocalSdpMunger';
5 7
 import { default as SampleSdpStrings } from './SampleSdpStrings.js';
6 8
 
@@ -17,9 +19,9 @@ function getSsrcLines(desc, mediaType) {
17 19
     return mline.ssrcs ?? [];
18 20
 }
19 21
 
20
-describe('TransformRecvOnly', () => {
22
+describe('TransformSdpsForUnifiedPlan', () => {
21 23
     let localSdpMunger;
22
-    const tpc = { id: '1' };
24
+    const tpc = new MockPeerConnection('1', true);
23 25
     const localEndpointId = 'sRdpsdg';
24 26
 
25 27
     beforeEach(() => {
@@ -58,7 +60,9 @@ describe('TransformRecvOnly', () => {
58 60
             expect(audioSsrcs.length).toEqual(4);
59 61
             expect(videoSsrcs.length).toEqual(6);
60 62
         });
63
+    });
61 64
 
65
+    describe('addMsids', () => {
62 66
         it('should add endpointId to msid', () => {
63 67
             const sdpStr = transform.write(SampleSdpStrings.firefoxSdp);
64 68
             const desc = new RTCSessionDescription({
@@ -79,7 +83,7 @@ describe('TransformRecvOnly', () => {
79 83
             }
80 84
         });
81 85
 
82
-        it('should add msid', () => {
86
+        it('should add missing msid', () => {
83 87
             // P2P case only.
84 88
             localSdpMunger.tpc.isP2P = true;
85 89
 
@@ -97,3 +101,32 @@ describe('TransformRecvOnly', () => {
97 101
         });
98 102
     });
99 103
 });
104
+
105
+describe('DoNotTransformSdpForPlanB', () => {
106
+    let localSdpMunger;
107
+    const tpc = new MockPeerConnection('1', false);
108
+    const localEndpointId = 'sRdpsdg';
109
+
110
+    beforeEach(() => {
111
+        localSdpMunger = new LocalSdpMunger(tpc, localEndpointId);
112
+    });
113
+    describe('stripSsrcs', () => {
114
+        beforeEach(() => { }); // eslint-disable-line no-empty-function
115
+        it('should not strip ssrcs from an sdp with no msid', () => {
116
+            localSdpMunger.tpc.isP2P = false;
117
+
118
+            const sdpStr = transform.write(SampleSdpStrings.recvOnlySdp);
119
+            const desc = new RTCSessionDescription({
120
+                type: 'offer',
121
+                sdp: sdpStr
122
+            });
123
+            const transformedDesc = localSdpMunger.transformStreamIdentifiers(desc);
124
+            const newSdp = transform.parse(transformedDesc.sdp);
125
+            const audioSsrcs = getSsrcLines(newSdp, 'audio');
126
+            const videoSsrcs = getSsrcLines(newSdp, 'video');
127
+
128
+            expect(audioSsrcs.length).toEqual(1);
129
+            expect(videoSsrcs.length).toEqual(1);
130
+        });
131
+    });
132
+});

+ 24
- 9
modules/xmpp/JingleSessionPC.js Переглянути файл

@@ -338,7 +338,10 @@ export default class JingleSessionPC extends JingleSession {
338 338
             = browser.supportsUnifiedPlan()
339 339
                 && (browser.isFirefox()
340 340
                     || browser.isWebKitBased()
341
-                    || (!this.isP2P && browser.isChromiumBased() && options.enableUnifiedOnChrome));
341
+                    || (browser.isChromiumBased()
342
+
343
+                        // Provide a way to control the behavior for jvb and p2p connections independently.
344
+                        && this.isP2P ? options.p2p?.enableUnifiedOnChrome : options.enableUnifiedOnChrome));
342 345
 
343 346
         if (this.isP2P) {
344 347
             // simulcast needs to be disabled for P2P (121) calls
@@ -1586,6 +1589,7 @@ export default class JingleSessionPC extends JingleSession {
1586 1589
      */
1587 1590
     _parseSsrcInfoFromSourceAdd(sourceAddElem, currentRemoteSdp) {
1588 1591
         const addSsrcInfo = [];
1592
+        const self = this;
1589 1593
 
1590 1594
         $(sourceAddElem).each((i1, content) => {
1591 1595
             const name = $(content).attr('name');
@@ -1605,10 +1609,8 @@ export default class JingleSessionPC extends JingleSession {
1605 1609
                             })
1606 1610
                             .get();
1607 1611
 
1608
-                    if (ssrcs.length) {
1609
-                        lines
1610
-                            += `a=ssrc-group:${semantics} ${
1611
-                                ssrcs.join(' ')}\r\n`;
1612
+                    if (ssrcs.length && !currentRemoteSdp.containsSSRC(ssrcs[0])) {
1613
+                        lines += `a=ssrc-group:${semantics} ${ssrcs.join(' ')}\r\n`;
1612 1614
                     }
1613 1615
                 });
1614 1616
 
@@ -1622,7 +1624,10 @@ export default class JingleSessionPC extends JingleSession {
1622 1624
                 const ssrc = $(this).attr('ssrc');
1623 1625
 
1624 1626
                 if (currentRemoteSdp.containsSSRC(ssrc)) {
1625
-                    logger.warn(`${this} Source-add request for existing SSRC: ${ssrc}`);
1627
+
1628
+                    // Do not print the warning for unified plan p2p case since ssrcs are never removed from the SDP.
1629
+                    !(self.usesUnifiedPlan && self.isP2P)
1630
+                        && logger.warn(`${self} Source-add request for existing SSRC: ${ssrc}`);
1626 1631
 
1627 1632
                     return;
1628 1633
                 }
@@ -1820,7 +1825,17 @@ export default class JingleSessionPC extends JingleSession {
1820 1825
                     const mid = remoteSdp.media.findIndex(mLine => mLine.includes(line));
1821 1826
 
1822 1827
                     if (mid > -1) {
1823
-                        remoteSdp.media[mid] = remoteSdp.media[mid].replace(`${line}\r\n`, '');
1828
+                        // Remove the ssrcs from the m-line in
1829
+                        // 1. Plan-b mode always.
1830
+                        // 2. Unified mode but only for jvb connection. In p2p mode if the ssrc is removed and added
1831
+                        // back to the same m-line, Chrome/Safari do not render the media even if it being received
1832
+                        // and decoded from the remote peer. The webrtc spec is not clear about m-line re-use and
1833
+                        // the browser vendors have implemented this differently. Currently workaround this by changing
1834
+                        // the media direction, that should be enough for the browser to fire the "removetrack" event
1835
+                        // on the associated MediaStream.
1836
+                        if (!this.usesUnifiedPlan || (this.usesUnifiedPlan && !this.isP2P)) {
1837
+                            remoteSdp.media[mid] = remoteSdp.media[mid].replace(`${line}\r\n`, '');
1838
+                        }
1824 1839
 
1825 1840
                         // The current direction of the transceiver for p2p will depend on whether a local sources is
1826 1841
                         // added or not. It will be 'sendrecv' if the local source is present, 'sendonly' otherwise.
@@ -1866,8 +1881,8 @@ export default class JingleSessionPC extends JingleSession {
1866 1881
         addSsrcInfo.forEach((lines, idx) => {
1867 1882
             remoteSdp.media[idx] += lines;
1868 1883
 
1869
-            // Make sure to change the direction to 'sendrecv' only for p2p connections. For jvb connections, a new
1870
-            // m-line is added for the new remote sources.
1884
+            // Make sure to change the direction to 'sendrecv/sendonly' only for p2p connections. For jvb connections,
1885
+            // a new m-line is added for the new remote sources.
1871 1886
             if (this.isP2P && this.usesUnifiedPlan) {
1872 1887
                 const mediaType = SDPUtil.parseMLine(remoteSdp.media[idx].split('\r\n')[0])?.media;
1873 1888
                 const desiredDirection = this.peerconnection.getDesiredMediaDirection(mediaType, true);

Завантаження…
Відмінити
Зберегти