瀏覽代碼

ref: store SSRCs as numbers (#485)

* ref: store SSRCs as a number

Converts all the places where SSRCs where stored as string to use
numbers.

* doc(RTPStatsCollector): getNonNegativeStat

Fixes invalid description about returning NaN.

* ref(JitsiConf...EventManager): simplify for..of

* ref(JitsiRemoteTrack): throw TypeError

Will throw a TypeError when 'ssrc' is not a number.

* fix(RTPStatsCollector): invalid reference

* doc(RTPStatsCollector): getNonNegativeStat private

* ref(RTPStatsCollector): simplify for..of

* fix(SSRCs): check for negative value

Will not accept negative SSRCs, since those are supposed to be unsigned.
tags/v0.0.2
Paweł Domas 8 年之前
父節點
當前提交
d03204a0c0

+ 18
- 35
JitsiConferenceEventManager.js 查看文件

53
  * @param resolutions map of resolutions by ssrc
53
  * @param resolutions map of resolutions by ssrc
54
  */
54
  */
55
 function mapResolutionsByUserId(conference, resolutions) {
55
 function mapResolutionsByUserId(conference, resolutions) {
56
-
57
     const id2resolution = {};
56
     const id2resolution = {};
58
 
57
 
59
     // preprocess resolutions: group by user id, skip incorrect
58
     // preprocess resolutions: group by user id, skip incorrect
60
     // resolutions etc.
59
     // resolutions etc.
61
-    Object.keys(resolutions).forEach(ssrc => {
62
-        const resolution = resolutions[ssrc];
63
-
64
-        if (!resolution.width || !resolution.height
65
-            || resolution.width === -1 || resolution.height === -1) {
66
-            return;
67
-        }
68
-
60
+    for (const [ ssrc, resolution ] of resolutions) {
69
         const id = conference.rtc.getResourceBySSRC(ssrc);
61
         const id = conference.rtc.getResourceBySSRC(ssrc);
70
 
62
 
71
-        if (!id) {
72
-            return;
73
-        }
74
-
75
-        // ssrc to resolution map for user id
76
-        const idResolutions = id2resolution[id] || {};
63
+        if (id
64
+            && resolution.width && resolution.height
65
+            && resolution.width !== -1 && resolution.height !== -1) {
66
+            // ssrc to resolution map for user id
67
+            const idResolutions = id2resolution[id] || {};
77
 
68
 
78
-        idResolutions[ssrc] = resolution;
69
+            idResolutions[ssrc] = resolution;
79
 
70
 
80
-        id2resolution[id] = idResolutions;
81
-    });
71
+            id2resolution[id] = idResolutions;
72
+        }
73
+    }
82
 
74
 
83
     return id2resolution;
75
     return id2resolution;
84
 }
76
 }
89
  * @param framerates map of framerates by ssrc
81
  * @param framerates map of framerates by ssrc
90
  */
82
  */
91
 function mapFrameratesByUserId(conference, framerates) {
83
 function mapFrameratesByUserId(conference, framerates) {
92
-
93
     const id2framerate = {};
84
     const id2framerate = {};
94
 
85
 
95
     // preprocess framerates: group by user id
86
     // preprocess framerates: group by user id
96
-    Object.keys(framerates).forEach(ssrc => {
97
-        const framerate = framerates[ssrc];
98
-
99
-        if (framerate === 0) {
100
-            return;
101
-        }
102
-
87
+    for (const [ ssrc, framerate ] of framerates) {
103
         const id = conference.rtc.getResourceBySSRC(ssrc);
88
         const id = conference.rtc.getResourceBySSRC(ssrc);
104
 
89
 
105
-        if (!id) {
106
-            return;
107
-        }
108
-
109
-        // ssrc to framerate map for user id
110
-        const id2framerates = id2framerate[id] || {};
90
+        if (framerate !== 0 && id) {
91
+            // ssrc to framerate map for user id
92
+            const id2framerates = id2framerate[id] || {};
111
 
93
 
112
-        id2framerates[ssrc] = framerate;
94
+            id2framerates[ssrc] = framerate;
113
 
95
 
114
-        id2framerate[id] = id2framerates;
115
-    });
96
+            id2framerate[id] = id2framerates;
97
+        }
98
+    }
116
 
99
 
117
     return id2framerate;
100
     return id2framerate;
118
 }
101
 }

+ 8
- 2
modules/RTC/JitsiRemoteTrack.js 查看文件

22
  *        the new JitsiRemoteTrack
22
  *        the new JitsiRemoteTrack
23
  * @param {MediaType} mediaType the type of the media
23
  * @param {MediaType} mediaType the type of the media
24
  * @param {VideoType} videoType the type of the video if applicable
24
  * @param {VideoType} videoType the type of the video if applicable
25
- * @param {string} ssrc the SSRC number of the Media Stream
25
+ * @param {number} ssrc the SSRC number of the Media Stream
26
  * @param {boolean} muted the initial muted state
26
  * @param {boolean} muted the initial muted state
27
  * @param {boolean} isP2P indicates whether or not this track belongs to a P2P
27
  * @param {boolean} isP2P indicates whether or not this track belongs to a P2P
28
  * session
28
  * session
29
+ * @throws {TypeError} if <tt>ssrc</tt> is not a number.
29
  * @constructor
30
  * @constructor
30
  */
31
  */
31
 export default function JitsiRemoteTrack(
32
 export default function JitsiRemoteTrack(
50
         mediaType,
51
         mediaType,
51
         videoType);
52
         videoType);
52
     this.rtc = rtc;
53
     this.rtc = rtc;
54
+
55
+    // Prevent from mixing up type of SSRC which should be a number
56
+    if (typeof ssrc !== 'number') {
57
+        throw new TypeError(`SSRC ${ssrc} is not a number`);
58
+    }
53
     this.ssrc = ssrc;
59
     this.ssrc = ssrc;
54
     this.ownerEndpointId = ownerEndpointId;
60
     this.ownerEndpointId = ownerEndpointId;
55
     this.muted = muted;
61
     this.muted = muted;
147
 
153
 
148
 /**
154
 /**
149
  * Returns the synchronization source identifier (SSRC) of this remote track.
155
  * Returns the synchronization source identifier (SSRC) of this remote track.
150
- * @returns {string} the SSRC of this remote track
156
+ * @returns {number} the SSRC of this remote track
151
  */
157
  */
152
 JitsiRemoteTrack.prototype.getSSRC = function() {
158
 JitsiRemoteTrack.prototype.getSSRC = function() {
153
     return this.ssrc;
159
     return this.ssrc;

+ 5
- 16
modules/RTC/RTC.js 查看文件

670
     /**
670
     /**
671
      * Searches in localTracks(session stores ssrc for audio and video) and
671
      * Searches in localTracks(session stores ssrc for audio and video) and
672
      * remoteTracks for the ssrc and returns the corresponding resource.
672
      * remoteTracks for the ssrc and returns the corresponding resource.
673
-     * @param ssrc the ssrc to check.
673
+     * @param {number} ssrc the ssrc to check.
674
      */
674
      */
675
     getResourceBySSRC(ssrc) {
675
     getResourceBySSRC(ssrc) {
676
         const track = this._getTrackBySSRC(ssrc);
676
         const track = this._getTrackBySSRC(ssrc);
680
 
680
 
681
     /**
681
     /**
682
      * Finds a track (either local or remote) which runs on the given SSRC.
682
      * Finds a track (either local or remote) which runs on the given SSRC.
683
-     * @param {string|number} ssrc
683
+     * @param {number} ssrc
684
      * @return {JitsiTrack|undefined}
684
      * @return {JitsiTrack|undefined}
685
-     *
686
-     * FIXME figure out where SSRC is stored as a string and convert to number
687
      * @private
685
      * @private
688
      */
686
      */
689
     _getTrackBySSRC(ssrc) {
687
     _getTrackBySSRC(ssrc) {
690
         let track
688
         let track
691
             = this.getLocalTracks().find(
689
             = this.getLocalTracks().find(
692
                 localTrack =>
690
                 localTrack =>
693
-
694
-                    // It is important that SSRC is not compared with ===,
695
-                    // because the code calling this method is inconsistent
696
-                    // about string vs number types
697
                     Array.from(this.peerConnections.values())
691
                     Array.from(this.peerConnections.values())
698
-                         .find(pc => pc.getLocalSSRC(localTrack) == ssrc) // eslint-disable-line eqeqeq, max-len
692
+                         .find(pc => pc.getLocalSSRC(localTrack) === ssrc)
699
                 );
693
                 );
700
 
694
 
701
         if (!track) {
695
         if (!track) {
708
     /**
702
     /**
709
      * Searches in remoteTracks for the ssrc and returns the corresponding
703
      * Searches in remoteTracks for the ssrc and returns the corresponding
710
      * track.
704
      * track.
711
-     * @param ssrc the ssrc to check.
705
+     * @param {number} ssrc the ssrc to check.
712
      * @return {JitsiRemoteTrack|undefined} return the first remote track that
706
      * @return {JitsiRemoteTrack|undefined} return the first remote track that
713
      * matches given SSRC or <tt>undefined</tt> if no such track was found.
707
      * matches given SSRC or <tt>undefined</tt> if no such track was found.
714
      * @private
708
      * @private
715
      */
709
      */
716
     _getRemoteTrackBySSRC(ssrc) {
710
     _getRemoteTrackBySSRC(ssrc) {
717
-        /* eslint-disable eqeqeq */
718
-        // FIXME: Convert the SSRCs in whole project to use the same type.
719
-        // Now we are using number and string.
720
         return this.getRemoteTracks().find(
711
         return this.getRemoteTracks().find(
721
-            remoteTrack => ssrc == remoteTrack.getSSRC());
722
-
723
-        /* eslint-enable eqeqeq */
712
+            remoteTrack => ssrc === remoteTrack.getSSRC());
724
     }
713
     }
725
 
714
 
726
     /**
715
     /**

+ 13
- 3
modules/RTC/TraceablePeerConnection.js 查看文件

575
 
575
 
576
     // FIXME the length of ssrcLines[0] not verified, but it will fail
576
     // FIXME the length of ssrcLines[0] not verified, but it will fail
577
     // with global error handler anyway
577
     // with global error handler anyway
578
-    const trackSsrc = ssrcLines[0].substring(7).split(' ')[0];
578
+    const ssrcStr = ssrcLines[0].substring(7).split(' ')[0];
579
+    const trackSsrc = Number(ssrcStr);
579
     const ownerEndpointId = this.signalingLayer.getSSRCOwner(trackSsrc);
580
     const ownerEndpointId = this.signalingLayer.getSSRCOwner(trackSsrc);
580
 
581
 
581
-    if (!ownerEndpointId) {
582
+    if (isNaN(trackSsrc) || trackSsrc < 0) {
583
+        GlobalOnErrorHandler.callErrorHandler(
584
+            new Error(
585
+                `Invalid SSRC: ${ssrcStr
586
+                    } for remote track, msid: ${streamId
587
+                    } media type: ${mediaType}`));
588
+
589
+        // Abort
590
+        return;
591
+    } else if (!ownerEndpointId) {
582
         GlobalOnErrorHandler.callErrorHandler(
592
         GlobalOnErrorHandler.callErrorHandler(
583
             new Error(
593
             new Error(
584
                 `No SSRC owner known for: ${trackSsrc
594
                 `No SSRC owner known for: ${trackSsrc
622
  * @param {MediaStreamTrack} track the WebRTC track instance
632
  * @param {MediaStreamTrack} track the WebRTC track instance
623
  * @param {MediaType} mediaType the track's type of the media
633
  * @param {MediaType} mediaType the track's type of the media
624
  * @param {VideoType} [videoType] the track's type of the video (if applicable)
634
  * @param {VideoType} [videoType] the track's type of the video (if applicable)
625
- * @param {string} ssrc the track's main SSRC number
635
+ * @param {number} ssrc the track's main SSRC number
626
  * @param {boolean} muted the initial muted status
636
  * @param {boolean} muted the initial muted status
627
  */
637
  */
628
 TraceablePeerConnection.prototype._createRemoteTrack
638
 TraceablePeerConnection.prototype._createRemoteTrack

+ 63
- 54
modules/statistics/RTPStatsCollector.js 查看文件

231
     this.statsIntervalId = null;
231
     this.statsIntervalId = null;
232
     this.statsIntervalMilis = statsInterval;
232
     this.statsIntervalMilis = statsInterval;
233
 
233
 
234
-    // Map of ssrcs to SsrcStats
235
-    this.ssrc2stats = {};
234
+    /**
235
+     * Maps SSRC numbers to {@link SsrcStats}.
236
+     * @type {Map<number,SsrcStats}
237
+     */
238
+    this.ssrc2stats = new Map();
236
 }
239
 }
237
 
240
 
238
 /* eslint-enable max-params */
241
 /* eslint-enable max-params */
401
     return (item, name) => itemStatByKey(item, keyFromName(name));
404
     return (item, name) => itemStatByKey(item, keyFromName(name));
402
 };
405
 };
403
 
406
 
407
+/**
408
+ * Obtains a stat value from given stat and converts it to a non-negative
409
+ * number. If the value is either invalid or negative then 0 will be returned.
410
+ * @param report
411
+ * @param {string} name
412
+ * @return {number}
413
+ * @private
414
+ */
415
+StatsCollector.prototype.getNonNegativeStat = function(report, name) {
416
+    let value = this._getStatValue(report, name);
417
+
418
+    if (typeof value !== 'number') {
419
+        value = Number(value);
420
+    }
421
+
422
+    if (isNaN(value)) {
423
+        return 0;
424
+    }
425
+
426
+    return Math.max(0, value);
427
+};
428
+
404
 /* eslint-disable no-continue */
429
 /* eslint-disable no-continue */
405
 
430
 
406
 /**
431
 /**
412
     }
437
     }
413
 
438
 
414
     const getStatValue = this._getStatValue;
439
     const getStatValue = this._getStatValue;
415
-
416
-    /**
417
-     *
418
-     * @param report
419
-     * @param name
420
-     */
421
-    function getNonNegativeStat(report, name) {
422
-        let value = getStatValue(report, name);
423
-
424
-        if (typeof value !== 'number') {
425
-            value = Number(value);
426
-        }
427
-
428
-        if (isNaN(value)) {
429
-            return 0;
430
-        }
431
-
432
-        return Math.max(0, value);
433
-    }
434
     const byteSentStats = {};
440
     const byteSentStats = {};
435
 
441
 
436
     for (const idx in this.currentStatsReport) {
442
     for (const idx in this.currentStatsReport) {
464
                 type = getStatValue(now, 'transportType');
470
                 type = getStatValue(now, 'transportType');
465
                 localip = getStatValue(now, 'localAddress');
471
                 localip = getStatValue(now, 'localAddress');
466
                 active = getStatValue(now, 'activeConnection');
472
                 active = getStatValue(now, 'activeConnection');
467
-                rtt = getNonNegativeStat(now, 'currentRoundTripTime');
473
+                rtt = this.getNonNegativeStat(now, 'currentRoundTripTime');
468
             } catch (e) { /* not supported*/ }
474
             } catch (e) { /* not supported*/ }
469
             if (!ip || !type || !localip || active !== 'true') {
475
             if (!ip || !type || !localip || active !== 'true') {
470
                 continue;
476
                 continue;
512
         }
518
         }
513
 
519
 
514
         const before = this.previousStatsReport[idx];
520
         const before = this.previousStatsReport[idx];
515
-        const ssrc = getStatValue(now, 'ssrc');
521
+        const ssrc = this.getNonNegativeStat(now, 'ssrc');
516
 
522
 
517
         if (!before || !ssrc) {
523
         if (!before || !ssrc) {
518
             continue;
524
             continue;
529
             continue;
535
             continue;
530
         }
536
         }
531
 
537
 
532
-        const ssrcStats
533
-          = this.ssrc2stats[ssrc] || (this.ssrc2stats[ssrc] = new SsrcStats());
538
+        let ssrcStats = this.ssrc2stats.get(ssrc);
539
+
540
+        if (!ssrcStats) {
541
+            ssrcStats = new SsrcStats();
542
+            this.ssrc2stats.set(ssrc, ssrcStats);
543
+        }
534
 
544
 
535
         let isDownloadStream = true;
545
         let isDownloadStream = true;
536
         let key = 'packetsReceived';
546
         let key = 'packetsReceived';
550
             packetsNow = 0;
560
             packetsNow = 0;
551
         }
561
         }
552
 
562
 
553
-        const packetsBefore = getNonNegativeStat(before, key);
563
+        const packetsBefore = this.getNonNegativeStat(before, key);
554
         const packetsDiff = Math.max(0, packetsNow - packetsBefore);
564
         const packetsDiff = Math.max(0, packetsNow - packetsBefore);
555
 
565
 
556
-        const packetsLostNow = getNonNegativeStat(now, 'packetsLost');
557
-        const packetsLostBefore = getNonNegativeStat(before, 'packetsLost');
566
+        const packetsLostNow
567
+            = this.getNonNegativeStat(now, 'packetsLost');
568
+        const packetsLostBefore
569
+            = this.getNonNegativeStat(before, 'packetsLost');
558
         const packetsLostDiff = Math.max(0, packetsLostNow - packetsLostBefore);
570
         const packetsLostDiff = Math.max(0, packetsLostNow - packetsLostBefore);
559
 
571
 
560
         ssrcStats.setLoss({
572
         ssrcStats.setLoss({
563
             isDownloadStream
575
             isDownloadStream
564
         });
576
         });
565
 
577
 
566
-        const bytesReceivedNow = getNonNegativeStat(now, 'bytesReceived');
567
-        const bytesReceivedBefore = getNonNegativeStat(before, 'bytesReceived');
578
+        const bytesReceivedNow
579
+            = this.getNonNegativeStat(now, 'bytesReceived');
580
+        const bytesReceivedBefore
581
+            = this.getNonNegativeStat(before, 'bytesReceived');
568
         const bytesReceived
582
         const bytesReceived
569
             = Math.max(0, bytesReceivedNow - bytesReceivedBefore);
583
             = Math.max(0, bytesReceivedNow - bytesReceivedBefore);
570
 
584
 
628
             // let's try with another one (FF)
642
             // let's try with another one (FF)
629
             try {
643
             try {
630
                 ssrcStats.setFramerate(Math.round(
644
                 ssrcStats.setFramerate(Math.round(
631
-                    getNonNegativeStat(now, 'framerateMean')));
645
+                    this.getNonNegativeStat(now, 'framerateMean')));
632
             } catch (err) { /* not supported*/ }
646
             } catch (err) { /* not supported*/ }
633
         }
647
         }
634
 
648
 
650
     };
664
     };
651
     let bitrateDownload = 0;
665
     let bitrateDownload = 0;
652
     let bitrateUpload = 0;
666
     let bitrateUpload = 0;
653
-    const resolutions = {};
654
-    const framerates = {};
655
-
656
-    Object.keys(this.ssrc2stats).forEach(
657
-        function(ssrc) {
658
-            const ssrcStats = this.ssrc2stats[ssrc];
667
+    const resolutions = new Map();
668
+    const framerates = new Map();
659
 
669
 
660
-            // process packet loss stats
661
-            const loss = ssrcStats.loss;
662
-            const type = loss.isDownloadStream ? 'download' : 'upload';
670
+    for (const [ ssrc, ssrcStats ] of this.ssrc2stats) {
671
+        // process packet loss stats
672
+        const loss = ssrcStats.loss;
673
+        const type = loss.isDownloadStream ? 'download' : 'upload';
663
 
674
 
664
-            totalPackets[type] += loss.packetsTotal;
665
-            lostPackets[type] += loss.packetsLost;
675
+        totalPackets[type] += loss.packetsTotal;
676
+        lostPackets[type] += loss.packetsLost;
666
 
677
 
667
-            // process bitrate stats
668
-            bitrateDownload += ssrcStats.bitrate.download;
669
-            bitrateUpload += ssrcStats.bitrate.upload;
678
+        // process bitrate stats
679
+        bitrateDownload += ssrcStats.bitrate.download;
680
+        bitrateUpload += ssrcStats.bitrate.upload;
670
 
681
 
671
-            ssrcStats.resetBitrate();
682
+        ssrcStats.resetBitrate();
672
 
683
 
673
-            // collect resolutions
674
-            resolutions[ssrc] = ssrcStats.resolution;
684
+        // collect resolutions
685
+        resolutions.set(ssrc, ssrcStats.resolution);
675
 
686
 
676
-            // collect framerates
677
-            framerates[ssrc] = ssrcStats.framerate;
678
-        },
679
-        this
680
-    );
687
+        // collect framerates
688
+        framerates.set(ssrc, ssrcStats.framerate);
689
+    }
681
 
690
 
682
     this.eventEmitter.emit(
691
     this.eventEmitter.emit(
683
         StatisticsEvents.BYTE_SENT_STATS, this.peerconnection, byteSentStats);
692
         StatisticsEvents.BYTE_SENT_STATS, this.peerconnection, byteSentStats);
728
         }
737
         }
729
 
738
 
730
         const before = this.baselineAudioLevelsReport[idx];
739
         const before = this.baselineAudioLevelsReport[idx];
731
-        const ssrc = getStatValue(now, 'ssrc');
740
+        const ssrc = this.getNonNegativeStat(now, 'ssrc');
732
 
741
 
733
         if (!before) {
742
         if (!before) {
734
             logger.warn(`${ssrc} not enough data`);
743
             logger.warn(`${ssrc} not enough data`);

+ 9
- 3
modules/xmpp/JingleSessionPC.js 查看文件

600
                         + 'source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
600
                         + 'source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
601
 
601
 
602
             ssrcs.each((i2, ssrcElement) => {
602
             ssrcs.each((i2, ssrcElement) => {
603
-                const ssrc = ssrcElement.getAttribute('ssrc');
603
+                const ssrc = Number(ssrcElement.getAttribute('ssrc'));
604
 
604
 
605
                 $(ssrcElement)
605
                 $(ssrcElement)
606
                     .find('>ssrc-info[xmlns="http://jitsi.org/jitmeet"]')
606
                     .find('>ssrc-info[xmlns="http://jitsi.org/jitmeet"]')
608
                         const owner = ssrcInfoElement.getAttribute('owner');
608
                         const owner = ssrcInfoElement.getAttribute('owner');
609
 
609
 
610
                         if (owner && owner.length) {
610
                         if (owner && owner.length) {
611
-                            this.signalingLayer.setSSRCOwner(
612
-                                ssrc, Strophe.getResourceFromJid(owner));
611
+                            if (isNaN(ssrc) || ssrc < 0) {
612
+                                logger.warn(
613
+                                    `Invalid SSRC ${ssrc} value received`
614
+                                        + ` for ${owner}`);
615
+                            } else {
616
+                                this.signalingLayer.setSSRCOwner(
617
+                                    ssrc, Strophe.getResourceFromJid(owner));
618
+                            }
613
                         }
619
                         }
614
                     }
620
                     }
615
                 );
621
                 );

+ 6
- 2
modules/xmpp/SignalingLayerImpl.js 查看文件

24
          * onaddstream webrtc event where we have only the ssrc
24
          * onaddstream webrtc event where we have only the ssrc
25
          * FIXME: This map got filled and never cleaned and can grow during long
25
          * FIXME: This map got filled and never cleaned and can grow during long
26
          * conference
26
          * conference
27
-         * @type {Map<string, string>} maps SSRC number to jid
27
+         * @type {Map<number, string>} maps SSRC number to jid
28
          */
28
          */
29
         this.ssrcOwners = new Map();
29
         this.ssrcOwners = new Map();
30
 
30
 
95
 
95
 
96
     /**
96
     /**
97
      * Set an SSRC owner.
97
      * Set an SSRC owner.
98
-     * @param {string} ssrc an SSRC to be owned
98
+     * @param {number} ssrc an SSRC to be owned
99
      * @param {string} endpointId owner's ID (MUC nickname)
99
      * @param {string} endpointId owner's ID (MUC nickname)
100
+     * @throws TypeError if <tt>ssrc</tt> is not a number
100
      */
101
      */
101
     setSSRCOwner(ssrc, endpointId) {
102
     setSSRCOwner(ssrc, endpointId) {
103
+        if (typeof ssrc !== 'number') {
104
+            throw new TypeError(`SSRC(${ssrc}) must be a number`);
105
+        }
102
         this.ssrcOwners.set(ssrc, endpointId);
106
         this.ssrcOwners.set(ssrc, endpointId);
103
     }
107
     }
104
 }
108
 }

+ 1
- 1
service/RTC/SignalingLayer.js 查看文件

19
 
19
 
20
     /**
20
     /**
21
      * Obtains the endpoint ID for given SSRC.
21
      * Obtains the endpoint ID for given SSRC.
22
-     * @param {string} ssrc a string representation of the SSRC number.
22
+     * @param {number} ssrc the SSRC number.
23
      * @return {string|null} the endpoint ID for given media SSRC.
23
      * @return {string|null} the endpoint ID for given media SSRC.
24
      */
24
      */
25
     getSSRCOwner(ssrc) { // eslint-disable-line no-unused-vars
25
     getSSRCOwner(ssrc) { // eslint-disable-line no-unused-vars

Loading…
取消
儲存