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

fix(sdp): group ssrcs when converting to jingle (#609)

Ssrcs not grouped together in the jingle element
are not recognized by the bridge. When parsing an
sdp with ssrcs that are not listed consecutively
it is important to still group them together
in the jingle element.
dev1
virtuacoplenny 8 роки тому
джерело
коміт
9f699bfeb5
3 змінених файлів з 117 додано та 67 видалено
  1. 14
    63
      modules/xmpp/SDP.js
  2. 95
    0
      modules/xmpp/SDP.spec.js
  3. 8
    4
      modules/xmpp/SDPUtil.js

+ 14
- 63
modules/xmpp/SDP.js Переглянути файл

1
-/* global $, APP */
1
+/* global $ */
2
 
2
 
3
 import SDPUtil from './SDPUtil';
3
 import SDPUtil from './SDPUtil';
4
 
4
 
266
             }
266
             }
267
 
267
 
268
             if (ssrc) {
268
             if (ssrc) {
269
-                // new style mapping
270
-                elem.c('source', { ssrc,
271
-                    xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
272
-
273
-                // FIXME: group by ssrc and support multiple different ssrcs
274
-                const ssrclines = SDPUtil.findLines(this.media[i], 'a=ssrc:');
275
-
276
-                if (ssrclines.length > 0) {
277
-                    // eslint-disable-next-line no-loop-func
278
-                    ssrclines.forEach(line => {
279
-                        const idx = line.indexOf(' ');
280
-                        const linessrc = line.substr(0, idx).substr(7);
281
-
282
-                        if (linessrc !== ssrc) {
283
-                            elem.up();
284
-                            ssrc = linessrc;
285
-                            elem.c('source', { ssrc,
286
-                                xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
287
-                        }
288
-                        const kv = line.substr(idx + 1);
269
+                const ssrcMap = SDPUtil.parseSSRC(this.media[i]);
270
+
271
+                for (const [ availableSsrc, ssrcParameters ] of ssrcMap) {
272
+                    elem.c('source', {
273
+                        ssrc: availableSsrc,
274
+                        xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'
275
+                    });
276
+
277
+                    ssrcParameters.forEach(ssrcSdpLine => {
278
+                        // get everything after first space
279
+                        const idx = ssrcSdpLine.indexOf(' ');
280
+                        const kv = ssrcSdpLine.substr(idx + 1);
289
 
281
 
290
                         elem.c('parameter');
282
                         elem.c('parameter');
291
                         if (kv.indexOf(':') === -1) {
283
                         if (kv.indexOf(':') === -1) {
302
                         }
294
                         }
303
                         elem.up();
295
                         elem.up();
304
                     });
296
                     });
305
-                } else {
306
-                    elem.up();
307
-                    elem.c('source', { ssrc,
308
-                        xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
309
-                    elem.c('parameter');
310
-                    elem.attrs({
311
-                        name: 'cname',
312
-
313
-                        // eslint-disable-next-line newline-per-chained-call
314
-                        value: Math.random().toString(36).substring(7)
315
-                    });
316
-                    elem.up();
317
-
318
-                    // FIXME what case does this code handle ? remove ???
319
-                    let msid = null;
320
-
321
-                    // FIXME what is this ? global APP.RTC in SDP ?
322
-                    const localTrack = APP.RTC.getLocalTracks(mline.media);
323
-
324
-                    // eslint-disable-next-line max-depth
325
-                    if (localTrack) {
326
-                        // FIXME before this changes the track id was accessed,
327
-                        // but msid stands for the stream id, makes no sense ?
328
-                        msid = localTrack.getTrackId();
329
-                    }
330
 
297
 
331
-                    // eslint-disable-next-line max-depth
332
-                    if (msid !== null) {
333
-                        msid = SDPUtil.filterSpecialChars(msid);
334
-                        elem.c('parameter');
335
-                        elem.attrs({ name: 'msid',
336
-                            value: msid });
337
-                        elem.up();
338
-                        elem.c('parameter');
339
-                        elem.attrs({ name: 'mslabel',
340
-                            value: msid });
341
-                        elem.up();
342
-                        elem.c('parameter');
343
-                        elem.attrs({ name: 'label',
344
-                            value: msid });
345
-                        elem.up();
346
-                    }
298
+                    elem.up();
347
                 }
299
                 }
348
-                elem.up();
349
 
300
 
350
                 // XEP-0339 handle ssrc-group attributes
301
                 // XEP-0339 handle ssrc-group attributes
351
                 const ssrcGroupLines
302
                 const ssrcGroupLines

+ 95
- 0
modules/xmpp/SDP.spec.js Переглянути файл

1
+import { $iq } from 'strophe.js';
2
+import SDP from './SDP';
3
+
4
+describe('SDP', () => {
5
+    describe('toJingle', () => {
6
+        /* eslint-disable max-len*/
7
+        const testSdp = [
8
+            'v=0\r\n',
9
+            'o=thisisadapterortc 2719486166053431 0 IN IP4 127.0.0.1\r\n',
10
+            's=-\r\n',
11
+            't=0 0\r\n',
12
+            'a=group:BUNDLE audio video\r\n',
13
+            'm=audio 9 UDP/TLS/RTP/SAVPF 111 126\r\n',
14
+            'c=IN IP4 0.0.0.0\r\n',
15
+            'a=rtpmap:111 opus/48000/2\r\n',
16
+            'a=rtpmap:126 telephone-event/8000\r\n',
17
+            'a=fmtp:111 minptime=10;useinbandfec=1\r\n',
18
+            'a=rtcp:9 IN IP4 0.0.0.0\r\n',
19
+            'a=setup:active\r\n',
20
+            'a=mid:audio\r\n',
21
+            'a=msid:26D16D51-503A-420B-8274-3DD1174E498F 8205D1FC-50B4-407C-87D5-9C45F1B779F0\r\n',
22
+            'a=sendrecv\r\n',
23
+            'a=ice-ufrag:tOQd\r\n',
24
+            'a=ice-pwd:3sAozs7hw6+2O6DBp2pt9fvY\r\n',
25
+            'a=fingerprint:sha-256 A9:00:CC:F9:81:33:EA:E9:E3:B4:01:E9:9E:18:B3:9B:F8:49:25:A0:5D:12:20:70:D5:6F:34:5A:2A:39:19:0A\r\n',
26
+            'a=ssrc:2002 msid:26D16D51-503A-420B-8274-3DD1174E498F 8205D1FC-50B4-407C-87D5-9C45F1B779F0\r\n',
27
+            'a=ssrc:2002 cname:juejgy8a01\r\n',
28
+            'a=rtcp-mux\r\n',
29
+            'm=video 9 UDP/TLS/RTP/SAVPF 107 100 99 96\r\n',
30
+            'c=IN IP4 0.0.0.0\r\n',
31
+            'a=rtpmap:107 h264/90000\r\n',
32
+            'a=rtpmap:100 VP8/90000\r\n',
33
+            'a=rtpmap:99 rtx/90000\r\n',
34
+            'a=rtpmap:96 rtx/90000\r\n',
35
+            'a=fmtp:107 x-google-start-bitrate=800\r\n',
36
+            'a=fmtp:100 x-google-start-bitrate=800\r\n',
37
+            'a=fmtp:99 apt=107\r\n',
38
+            'a=fmtp:96 apt=100\r\n',
39
+            'a=rtcp:9 IN IP4 0.0.0.0\r\n',
40
+            'a=rtcp-fb:107 nack\r\n',
41
+            'a=rtcp-fb:107 nack pli\r\n',
42
+            'a=rtcp-fb:107 goog-remb\r\n',
43
+            'a=rtcp-fb:100 nack\r\n',
44
+            'a=rtcp-fb:100 nack pli\r\n',
45
+            'a=rtcp-fb:100 goog-remb\r\n',
46
+            'a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n',
47
+            'a=setup:active\r\n',
48
+            'a=mid:video\r\n',
49
+            'a=msid:7C0035E5-2DA1-4AEA-804A-9E75BF9B3768 225E9CDA-0384-4C92-92DD-E74C1153EC68\r\n',
50
+            'a=sendrecv\r\n',
51
+            'a=ice-ufrag:tOQd\r\n',
52
+            'a=ice-pwd:3sAozs7hw6+2O6DBp2pt9fvY\r\n',
53
+            'a=fingerprint:sha-256 A9:00:CC:F9:81:33:EA:E9:E3:B4:01:E9:9E:18:B3:9B:F8:49:25:A0:5D:12:20:70:D5:6F:34:5A:2A:39:19:0A\r\n',
54
+            'a=ssrc:4004 msid:7C0035E5-2DA1-4AEA-804A-9E75BF9B3768 225E9CDA-0384-4C92-92DD-E74C1153EC68\r\n',
55
+            'a=ssrc:4005 msid:7C0035E5-2DA1-4AEA-804A-9E75BF9B3768 225E9CDA-0384-4C92-92DD-E74C1153EC68\r\n',
56
+            'a=ssrc:4004 cname:juejgy8a01\r\n',
57
+            'a=ssrc:4005 cname:juejgy8a01\r\n',
58
+            'a=ssrc-group:FID 4004 4005\r\n',
59
+            'a=rtcp-mux\r\n'
60
+        ].join('');
61
+        /* eslint-enable max-len*/
62
+
63
+        it('correctly groups ssrcs lines that are not in order', () => {
64
+            const sdp = new SDP(testSdp);
65
+            const accept = $iq({
66
+                to: 'peerjid',
67
+                type: 'set'
68
+            })
69
+            .c('jingle', {
70
+                xmlns: 'urn:xmpp:jingle:1',
71
+                action: 'session-accept',
72
+                initiator: false,
73
+                responder: true,
74
+                sid: 'temp-sid'
75
+            });
76
+
77
+            sdp.toJingle(accept, false);
78
+
79
+            const { nodeTree } = accept;
80
+            const descriptions
81
+                = Array.from(nodeTree.getElementsByTagName('description'));
82
+            const videoDescriptions = descriptions.filter(description =>
83
+                description.getAttribute('media') === 'video');
84
+            const count = videoDescriptions.reduce((iterator, description) => {
85
+                const childNodes = Array.from(description.childNodes);
86
+                const childNodesSources = childNodes.filter(child =>
87
+                    child.nodeName === 'source');
88
+
89
+                return iterator + childNodesSources.length;
90
+            }, 0);
91
+
92
+            expect(count).toBe(2);
93
+        });
94
+    });
95
+});

+ 8
- 4
modules/xmpp/SDPUtil.js Переглянути файл

241
         // proprietary mapping of a=ssrc lines
241
         // proprietary mapping of a=ssrc lines
242
         // TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher
242
         // TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher
243
         // on google docs and parse according to that
243
         // on google docs and parse according to that
244
-        const data = {};
244
+        const data = new Map();
245
         const lines = desc.split('\r\n');
245
         const lines = desc.split('\r\n');
246
 
246
 
247
         for (let i = 0; i < lines.length; i++) {
247
         for (let i = 0; i < lines.length; i++) {
248
             if (lines[i].substring(0, 7) === 'a=ssrc:') {
248
             if (lines[i].substring(0, 7) === 'a=ssrc:') {
249
-                const idx = lines[i].indexOf(' ');
249
+                // FIXME: Use regex to smartly find the ssrc.
250
+                const ssrc = lines[i].split('a=ssrc:')[1].split(' ')[0];
251
+
252
+                if (!data.get(ssrc)) {
253
+                    data.set(ssrc, []);
254
+                }
250
 
255
 
251
-                data[lines[i].substr(idx + 1).split(':', 2)[0]]
252
-                    = lines[i].substr(idx + 1).split(':', 2)[1];
256
+                data.get(ssrc).push(lines[i]);
253
             }
257
             }
254
         }
258
         }
255
 
259
 

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