Browse Source

Feat ssrc rewriting (#2192)

release-8443
Jaya Allamsetty 2 years ago
parent
commit
5713cb0d6d
No account linked to committer's email address

+ 31
- 15
JitsiConference.js View File

53
 import CodecMimeType from './service/RTC/CodecMimeType';
53
 import CodecMimeType from './service/RTC/CodecMimeType';
54
 import { MediaType } from './service/RTC/MediaType';
54
 import { MediaType } from './service/RTC/MediaType';
55
 import RTCEvents from './service/RTC/RTCEvents';
55
 import RTCEvents from './service/RTC/RTCEvents';
56
-import { getSourceNameForJitsiTrack } from './service/RTC/SignalingLayer';
56
+import { SignalingEvents } from './service/RTC/SignalingEvents';
57
+import { getMediaTypeFromSourceName, getSourceNameForJitsiTrack } from './service/RTC/SignalingLayer';
57
 import { VideoType } from './service/RTC/VideoType';
58
 import { VideoType } from './service/RTC/VideoType';
58
 import {
59
 import {
59
     ACTION_JINGLE_RESTART,
60
     ACTION_JINGLE_RESTART,
156
     this.eventEmitter = new EventEmitter();
157
     this.eventEmitter = new EventEmitter();
157
     this.options = options;
158
     this.options = options;
158
     this.eventManager = new JitsiConferenceEventManager(this);
159
     this.eventManager = new JitsiConferenceEventManager(this);
159
-    this.participants = {};
160
+
161
+    /**
162
+     * List of all the participants in the conference.
163
+     * @type {Map<string, JitsiParticipant>};
164
+     */
165
+    this.participants = new Map();
160
 
166
 
161
     /**
167
     /**
162
      * The signaling layer instance.
168
      * The signaling layer instance.
389
     );
395
     );
390
 
396
 
391
     this._signalingLayer.setChatRoom(this.room);
397
     this._signalingLayer.setChatRoom(this.room);
398
+    this._signalingLayer.on(
399
+        SignalingEvents.SOURCE_UPDATED,
400
+        (sourceName, endpointId, muted, videoType) => {
401
+            const participant = this.participants.get(endpointId);
402
+            const mediaType = getMediaTypeFromSourceName(sourceName);
403
+
404
+            if (participant) {
405
+                participant._setSources(mediaType, muted, sourceName, videoType);
406
+                this.eventEmitter.emit(JitsiConferenceEvents.PARTICIPANT_SOURCE_UPDATED, participant);
407
+            }
408
+        });
392
 
409
 
393
     // Connection interrupted/restored listeners
410
     // Connection interrupted/restored listeners
394
     this._onIceConnectionInterrupted
411
     this._onIceConnectionInterrupted
1198
     if (this.mutedByFocusActor && track.isAudioTrack()) {
1215
     if (this.mutedByFocusActor && track.isAudioTrack()) {
1199
         const actorId = Strophe.getResourceFromJid(this.mutedByFocusActor);
1216
         const actorId = Strophe.getResourceFromJid(this.mutedByFocusActor);
1200
 
1217
 
1201
-        actorParticipant = this.participants[actorId];
1218
+        actorParticipant = this.participants.get(actorId);
1202
     } else if (this.mutedVideoByFocusActor && track.isVideoTrack()) {
1219
     } else if (this.mutedVideoByFocusActor && track.isVideoTrack()) {
1203
         const actorId = Strophe.getResourceFromJid(this.mutedVideoByFocusActor);
1220
         const actorId = Strophe.getResourceFromJid(this.mutedVideoByFocusActor);
1204
 
1221
 
1205
-        actorParticipant = this.participants[actorId];
1222
+        actorParticipant = this.participants.get(actorId);
1206
     }
1223
     }
1207
 
1224
 
1208
     // Send the video type message to the bridge if the track is not removed/added to the pc as part of
1225
     // Send the video type message to the bridge if the track is not removed/added to the pc as part of
1631
 };
1648
 };
1632
 
1649
 
1633
 /**
1650
 /**
1634
- * @return Array<JitsiParticipant> an array of all participants in this
1635
- * conference.
1651
+ * @return Array<JitsiParticipant> an array of all participants in this conference.
1636
  */
1652
  */
1637
 JitsiConference.prototype.getParticipants = function() {
1653
 JitsiConference.prototype.getParticipants = function() {
1638
-    return Object.values(this.participants);
1654
+    return Array.from(this.participants.values());
1639
 };
1655
 };
1640
 
1656
 
1641
 /**
1657
 /**
1661
  * @param id the id of the participant.
1677
  * @param id the id of the participant.
1662
  */
1678
  */
1663
 JitsiConference.prototype.getParticipantById = function(id) {
1679
 JitsiConference.prototype.getParticipantById = function(id) {
1664
-    return this.participants[id];
1680
+    return this.participants.get(id);
1665
 };
1681
 };
1666
 
1682
 
1667
 /**
1683
 /**
1809
         participant._tracks.push(track);
1825
         participant._tracks.push(track);
1810
     }
1826
     }
1811
 
1827
 
1812
-    this.participants[id] = participant;
1828
+    this.participants.set(id, participant);
1813
     this.eventEmitter.emit(
1829
     this.eventEmitter.emit(
1814
         JitsiConferenceEvents.USER_JOINED,
1830
         JitsiConferenceEvents.USER_JOINED,
1815
         id,
1831
         id,
1922
         });
1938
         });
1923
     }
1939
     }
1924
 
1940
 
1925
-    const participant = this.participants[id];
1941
+    const participant = this.participants.get(id);
1926
 
1942
 
1927
     if (participant) {
1943
     if (participant) {
1928
-        delete this.participants[id];
1944
+        this.participants.delete(id);
1929
         this.eventEmitter.emit(JitsiConferenceEvents.USER_LEFT, id, participant, reason);
1945
         this.eventEmitter.emit(JitsiConferenceEvents.USER_LEFT, id, participant, reason);
1930
     }
1946
     }
1931
 
1947
 
1962
         return;
1978
         return;
1963
     }
1979
     }
1964
 
1980
 
1965
-    const actorParticipant = this.participants[actorId];
1981
+    const actorParticipant = this.participants.get(actorId);
1966
 
1982
 
1967
     if (isSelfPresence) {
1983
     if (isSelfPresence) {
1968
         this.eventEmitter.emit(
1984
         this.eventEmitter.emit(
1973
         return;
1989
         return;
1974
     }
1990
     }
1975
 
1991
 
1976
-    const kickedParticipant = this.participants[kickedParticipantId];
1992
+    const kickedParticipant = this.participants.get(kickedParticipantId);
1977
 
1993
 
1978
     kickedParticipant.setIsReplaced(isReplaceParticipant);
1994
     kickedParticipant.setIsReplaced(isReplaceParticipant);
1979
 
1995
 
2999
 
3015
 
3000
     let remoteID = Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);
3016
     let remoteID = Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);
3001
 
3017
 
3002
-    const participant = this.participants[remoteID];
3018
+    const participant = this.participants.get(remoteID);
3003
 
3019
 
3004
     if (participant) {
3020
     if (participant) {
3005
         remoteID = participant.getStatsID() || remoteID;
3021
         remoteID = participant.getStatsID() || remoteID;
3367
 
3383
 
3368
     let remoteID = Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);
3384
     let remoteID = Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);
3369
 
3385
 
3370
-    const participant = this.participants[remoteID];
3386
+    const participant = this.participants.get(remoteID);
3371
 
3387
 
3372
     if (participant) {
3388
     if (participant) {
3373
         remoteID = participant.getStatsID() || remoteID;
3389
         remoteID = participant.getStatsID() || remoteID;

+ 6
- 6
JitsiConferenceEventManager.js View File

574
     });
574
     });
575
 
575
 
576
     rtc.addListener(RTCEvents.VIDEO_SSRCS_REMAPPED, msg => {
576
     rtc.addListener(RTCEvents.VIDEO_SSRCS_REMAPPED, msg => {
577
-        const sess = this.conference.getActiveMediaSession();
578
-
579
-        sess.videoSsrcsRemapped(msg);
577
+        for (const session of this.conference.getMediaSessions()) {
578
+            session.processSourceMap(msg, MediaType.VIDEO);
579
+        }
580
     });
580
     });
581
 
581
 
582
     rtc.addListener(RTCEvents.AUDIO_SSRCS_REMAPPED, msg => {
582
     rtc.addListener(RTCEvents.AUDIO_SSRCS_REMAPPED, msg => {
583
-        const sess = this.conference.getActiveMediaSession();
584
-
585
-        sess.audioSsrcsRemapped(msg);
583
+        for (const session of this.conference.getMediaSessions()) {
584
+            session.processSourceMap(msg, MediaType.AUDIO);
585
+        }
586
     });
586
     });
587
 
587
 
588
     rtc.addListener(RTCEvents.ENDPOINT_MESSAGE_RECEIVED,
588
     rtc.addListener(RTCEvents.ENDPOINT_MESSAGE_RECEIVED,

+ 2
- 0
JitsiConferenceEvents.spec.ts View File

31
         JVB121_STATUS,
31
         JVB121_STATUS,
32
         KICKED,
32
         KICKED,
33
         PARTICIPANT_KICKED,
33
         PARTICIPANT_KICKED,
34
+        PARTICIPANT_SOURCE_UPDATED,
34
         LAST_N_ENDPOINTS_CHANGED,
35
         LAST_N_ENDPOINTS_CHANGED,
35
         FORWARDED_SOURCES_CHANGED,
36
         FORWARDED_SOURCES_CHANGED,
36
         LOCK_STATE_CHANGED,
37
         LOCK_STATE_CHANGED,
108
         expect( JVB121_STATUS ).toBe( 'conference.jvb121Status' );
109
         expect( JVB121_STATUS ).toBe( 'conference.jvb121Status' );
109
         expect( KICKED ).toBe( 'conference.kicked' );
110
         expect( KICKED ).toBe( 'conference.kicked' );
110
         expect( PARTICIPANT_KICKED ).toBe( 'conference.participant_kicked' );
111
         expect( PARTICIPANT_KICKED ).toBe( 'conference.participant_kicked' );
112
+        expect( PARTICIPANT_SOURCE_UPDATED ).toBe( 'conference.participant_source_updated' );
111
         expect( LAST_N_ENDPOINTS_CHANGED ).toBe( 'conference.lastNEndpointsChanged' );
113
         expect( LAST_N_ENDPOINTS_CHANGED ).toBe( 'conference.lastNEndpointsChanged' );
112
         expect( FORWARDED_SOURCES_CHANGED ).toBe( 'conference.forwardedSourcesChanged' );
114
         expect( FORWARDED_SOURCES_CHANGED ).toBe( 'conference.forwardedSourcesChanged' );
113
         expect( LOCK_STATE_CHANGED ).toBe( 'conference.lock_state_changed' );
115
         expect( LOCK_STATE_CHANGED ).toBe( 'conference.lock_state_changed' );

+ 6
- 0
JitsiConferenceEvents.ts View File

241
      */
241
      */
242
     PARTICIPANT_PROPERTY_CHANGED = 'conference.participant_property_changed',
242
     PARTICIPANT_PROPERTY_CHANGED = 'conference.participant_property_changed',
243
 
243
 
244
+    /**
245
+     * Indicates the state of sources attached to a given remote participant has changed.
246
+     */
247
+    PARTICIPANT_SOURCE_UPDATED = 'conference.participant_source_updated',
248
+
244
     /**
249
     /**
245
      * Indicates that the conference has switched between JVB and P2P connections.
250
      * Indicates that the conference has switched between JVB and P2P connections.
246
      * The first argument of this event is a <tt>boolean</tt> which when set to
251
      * The first argument of this event is a <tt>boolean</tt> which when set to
490
 export const JVB121_STATUS = JitsiConferenceEvents.JVB121_STATUS;
495
 export const JVB121_STATUS = JitsiConferenceEvents.JVB121_STATUS;
491
 export const KICKED = JitsiConferenceEvents.KICKED;
496
 export const KICKED = JitsiConferenceEvents.KICKED;
492
 export const PARTICIPANT_KICKED = JitsiConferenceEvents.PARTICIPANT_KICKED;
497
 export const PARTICIPANT_KICKED = JitsiConferenceEvents.PARTICIPANT_KICKED;
498
+export const PARTICIPANT_SOURCE_UPDATED = JitsiConferenceEvents.PARTICIPANT_SOURCE_UPDATED;
493
 export const LAST_N_ENDPOINTS_CHANGED = JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED;
499
 export const LAST_N_ENDPOINTS_CHANGED = JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED;
494
 export const FORWARDED_SOURCES_CHANGED = JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED;
500
 export const FORWARDED_SOURCES_CHANGED = JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED;
495
 export const LOCK_STATE_CHANGED = JitsiConferenceEvents.LOCK_STATE_CHANGED;
501
 export const LOCK_STATE_CHANGED = JitsiConferenceEvents.LOCK_STATE_CHANGED;

+ 47
- 0
JitsiParticipant.js View File

43
         this._isReplacing = isReplacing;
43
         this._isReplacing = isReplacing;
44
         this._isReplaced = isReplaced;
44
         this._isReplaced = isReplaced;
45
         this._features = new Set();
45
         this._features = new Set();
46
+
47
+        /**
48
+         * Remote sources associated with the participant in the following format.
49
+         * Map<mediaType, Map<sourceName, sourceInfo>>
50
+         *
51
+         * mediaType - 'audio' or 'video'.
52
+         * sourceName - name of the remote source.
53
+         * sourceInfo: {
54
+         *   muted: boolean;
55
+         *   videoType: string;
56
+         * }
57
+         */
58
+        this._sources = new Map();
46
     }
59
     }
47
 
60
 
48
     /**
61
     /**
61
             true);
74
             true);
62
     }
75
     }
63
 
76
 
77
+    /**
78
+     * Sets source info.
79
+     * @param {MediaType} mediaType The media type, 'audio' or 'video'.
80
+     * @param {boolean} muted The new muted state.
81
+     * @param {string} sourceName The name of the source.
82
+     * @param {string} videoType The video type of the source.
83
+     * @returns {void}
84
+     */
85
+    _setSources(mediaType, muted, sourceName, videoType) {
86
+        let sourceByMediaType = this._sources.get(mediaType);
87
+        const sourceInfo = {
88
+            muted,
89
+            videoType
90
+        };
91
+
92
+        if (sourceByMediaType?.size) {
93
+            sourceByMediaType.set(sourceName, sourceInfo);
94
+
95
+            return;
96
+        }
97
+
98
+        sourceByMediaType = new Map();
99
+        sourceByMediaType.set(sourceName, sourceInfo);
100
+        this._sources.set(mediaType, sourceByMediaType);
101
+    }
102
+
64
     /**
103
     /**
65
      * Returns the bot type for the participant.
104
      * Returns the bot type for the participant.
66
      *
105
      *
130
         return this._role;
169
         return this._role;
131
     }
170
     }
132
 
171
 
172
+    /**
173
+     * Returns the sources associated with this participant.
174
+     * @returns Map<string, Map<string, Object>>
175
+     */
176
+    getSources() {
177
+        return this._sources;
178
+    }
179
+
133
     /**
180
     /**
134
      * @returns {String} The stats ID of this participant.
181
      * @returns {String} The stats ID of this participant.
135
      */
182
      */

+ 56
- 82
modules/xmpp/JingleSessionPC.js View File

6
 import * as CodecMimeType from '../../service/RTC/CodecMimeType';
6
 import * as CodecMimeType from '../../service/RTC/CodecMimeType';
7
 import { MediaDirection } from '../../service/RTC/MediaDirection';
7
 import { MediaDirection } from '../../service/RTC/MediaDirection';
8
 import { MediaType } from '../../service/RTC/MediaType';
8
 import { MediaType } from '../../service/RTC/MediaType';
9
+import { VideoType } from '../../service/RTC/VideoType';
9
 import {
10
 import {
10
     ICE_DURATION,
11
     ICE_DURATION,
11
     ICE_STATE_CHANGED
12
     ICE_STATE_CHANGED
1793
     }
1794
     }
1794
 
1795
 
1795
     /**
1796
     /**
1796
-     * Filter remapped SSRCs.
1797
-     * Process owner change for existing SSRCs.
1798
-     * Return new ones for further processing.
1797
+     * Processes the source map message received from the bridge and creates a new remote track for newly signaled
1798
+     * SSRCs or updates the source-name and owner on the remote track for an existing SSRC.
1799
+     *
1800
+     * @param {Object} message - The source map message.
1801
+     * @param {string} mediaType - The media type, 'audio' or 'video'.
1802
+     * @returns {void}
1799
      */
1803
      */
1800
-    getNewSources(msg) {
1804
+    processSourceMap(message, mediaType) {
1801
         const newSources = [];
1805
         const newSources = [];
1802
 
1806
 
1803
-        for (const s of msg.mappedSources) {
1804
-            if (this.peerconnection.addRemoteSsrc(s.ssrc)) {
1805
-                logger.debug(`New SSRC ${s.ssrc}`);
1806
-                newSources[newSources.length] = s;
1807
+        for (const src of message.mappedSources) {
1808
+            if (this.peerconnection.addRemoteSsrc(src.ssrc)) {
1809
+                newSources.push(src);
1807
             } else {
1810
             } else {
1808
-                const track = this.peerconnection.getTrackBySSRC(s.ssrc);
1811
+                const { owner, source, ssrc, videoType } = src;
1812
+                const track = this.peerconnection.getTrackBySSRC(ssrc);
1809
 
1813
 
1810
                 if (track) {
1814
                 if (track) {
1811
-                    logger.debug(`Existing SSRC ${s.ssrc}: new owner ${s.owner}. name=${s.source}`);
1815
+                    logger.debug(`Existing SSRC ${ssrc}: new owner=${owner}, source-name=${source}`);
1812
 
1816
 
1813
                     // Update the SSRC owner.
1817
                     // Update the SSRC owner.
1814
-                    this._signalingLayer.setSSRCOwner(s.ssrc, s.owner);
1818
+                    this._signalingLayer.setSSRCOwner(ssrc, owner);
1815
 
1819
 
1816
-                    if (s.videoType === 'CAMERA') {
1817
-                        track._setVideoType('camera');
1818
-                    } else if (s.videoType === 'DESKTOP') {
1819
-                        track._setVideoType('desktop');
1820
-                    }
1820
+                    // Update the track with all the relevant info.
1821
+                    track.setSourceName(source);
1822
+                    track.setOwner(owner);
1823
+                    if (mediaType === MediaType.VIDEO) {
1824
+                        const type = videoType === 'CAMERA' ? VideoType.CAMERA : VideoType.DESKTOP;
1821
 
1825
 
1822
-                    track.setSourceName(s.source);
1823
-                    track.setOwner(s.owner);
1826
+                        track._setVideoType(type);
1827
+                    }
1824
                 } else {
1828
                 } else {
1825
-                    logger.error(`Remapped SSRC ${s.ssrc} not found`);
1829
+                    logger.error(`Remote track attached to a remote SSRC=${ssrc} not found`);
1826
                 }
1830
                 }
1827
             }
1831
             }
1828
         }
1832
         }
1829
 
1833
 
1830
-        return newSources;
1831
-    }
1832
-
1833
-    /**
1834
-     * Process SSRC remappings for video sources.
1835
-     */
1836
-    videoSsrcsRemapped(msg) {
1837
-        const newSources = this.getNewSources(msg);
1838
-
1839
-        if (newSources.length > 0) {
1840
-
1834
+        // Add the new SSRCs to the remote description by generating a source message.
1835
+        if (newSources.length) {
1841
             let node = $build('content', {
1836
             let node = $build('content', {
1842
                 xmlns: 'urn:xmpp:jingle:1',
1837
                 xmlns: 'urn:xmpp:jingle:1',
1843
-                name: 'video'
1838
+                name: mediaType
1844
             }).c('description', {
1839
             }).c('description', {
1845
                 xmlns: 'urn:xmpp:jingle:apps:rtp:1',
1840
                 xmlns: 'urn:xmpp:jingle:apps:rtp:1',
1846
-                media: MediaType.VIDEO
1841
+                media: mediaType
1847
             });
1842
             });
1848
 
1843
 
1849
-            for (const s of newSources) {
1850
-                const idx = ++this.numRemoteVideoSources;
1851
-                const msid = `remote-video-${idx} remote-video-${idx}`;
1844
+            for (const src of newSources) {
1845
+                const { rtx, ssrc } = src;
1846
+                let msid;
1852
 
1847
 
1853
-                _addSourceElement(node, s, s.ssrc, msid);
1854
-                if (s.rtx !== '-1') {
1855
-                    _addSourceElement(node, s, s.rtx, msid);
1856
-                    node.c('ssrc-group', {
1857
-                        xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',
1858
-                        semantics: 'FID'
1859
-                    })
1860
-                        .c('source', {
1861
-                            xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',
1862
-                            ssrc: s.ssrc
1863
-                        })
1864
-                        .up()
1865
-                        .c('source', {
1866
-                            xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',
1867
-                            ssrc: s.rtx
1868
-                        })
1869
-                        .up()
1870
-                        .up();
1871
-                }
1872
-            }
1848
+                if (mediaType === MediaType.VIDEO) {
1849
+                    const idx = ++this.numRemoteVideoSources;
1873
 
1850
 
1874
-            node = node.up();
1851
+                    msid = `remote-video-${idx} remote-video-${idx}`;
1875
 
1852
 
1876
-            this._addOrRemoveRemoteStream(true /* add */, node.node);
1877
-        }
1878
-    }
1879
-
1880
-    /**
1881
-     * Process SSRC remappings for audio sources.
1882
-     */
1883
-    audioSsrcsRemapped(msg) {
1884
-        const newSources = this.getNewSources(msg);
1885
-
1886
-        if (newSources.length > 0) {
1887
-
1888
-            let node = $build('content', {
1889
-                xmlns: 'urn:xmpp:jingle:1',
1890
-                name: 'audio'
1891
-            }).c('description', {
1892
-                xmlns: 'urn:xmpp:jingle:apps:rtp:1',
1893
-                media: MediaType.AUDIO
1894
-            });
1895
-
1896
-            for (const s of newSources) {
1897
-                const idx = ++this.numRemoteAudioSources;
1898
-                const msid = `remote-audio-${idx} remote-audio-${idx}`;
1853
+                    if (rtx !== '-1') {
1854
+                        _addSourceElement(node, src, rtx, msid);
1855
+                        node.c('ssrc-group', {
1856
+                            xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',
1857
+                            semantics: 'FID'
1858
+                        })
1859
+                            .c('source', {
1860
+                                xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',
1861
+                                ssrc
1862
+                            })
1863
+                            .up()
1864
+                            .c('source', {
1865
+                                xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',
1866
+                                ssrc: rtx
1867
+                            })
1868
+                            .up()
1869
+                            .up();
1870
+                    }
1871
+                } else {
1872
+                    const idx = ++this.numRemoteAudioSources;
1899
 
1873
 
1900
-                _addSourceElement(node, s, s.ssrc, msid);
1874
+                    msid = `remote-audio-${idx} remote-audio-${idx}`;
1875
+                }
1876
+                _addSourceElement(node, src, ssrc, msid);
1901
             }
1877
             }
1902
-
1903
             node = node.up();
1878
             node = node.up();
1904
-
1905
             this._addOrRemoveRemoteStream(true /* add */, node.node);
1879
             this._addOrRemoveRemoteStream(true /* add */, node.node);
1906
         }
1880
         }
1907
     }
1881
     }

+ 12
- 0
modules/xmpp/SignalingLayerImpl.js View File

142
                 = this._remoteSourceState[endpointId] || (this._remoteSourceState[endpointId] = {});
142
                 = this._remoteSourceState[endpointId] || (this._remoteSourceState[endpointId] = {});
143
 
143
 
144
             for (const sourceName of Object.keys(sourceInfoJSON)) {
144
             for (const sourceName of Object.keys(sourceInfoJSON)) {
145
+                let sourceChanged = false;
145
                 const mediaType = getMediaTypeFromSourceName(sourceName);
146
                 const mediaType = getMediaTypeFromSourceName(sourceName);
146
                 const newMutedState = Boolean(sourceInfoJSON[sourceName].muted);
147
                 const newMutedState = Boolean(sourceInfoJSON[sourceName].muted);
147
                 const oldSourceState = endpointSourceState[sourceName]
148
                 const oldSourceState = endpointSourceState[sourceName]
148
                     || (endpointSourceState[sourceName] = { sourceName });
149
                     || (endpointSourceState[sourceName] = { sourceName });
149
 
150
 
150
                 if (oldSourceState.muted !== newMutedState) {
151
                 if (oldSourceState.muted !== newMutedState) {
152
+                    sourceChanged = true;
151
                     oldSourceState.muted = newMutedState;
153
                     oldSourceState.muted = newMutedState;
152
                     if (emitEventsFromHere && !this._localSourceState[sourceName]) {
154
                     if (emitEventsFromHere && !this._localSourceState[sourceName]) {
153
                         this.eventEmitter.emit(SignalingEvents.SOURCE_MUTED_CHANGED, sourceName, newMutedState);
155
                         this.eventEmitter.emit(SignalingEvents.SOURCE_MUTED_CHANGED, sourceName, newMutedState);
161
 
163
 
162
                 if (oldSourceState.videoType !== newVideoType) {
164
                 if (oldSourceState.videoType !== newVideoType) {
163
                     oldSourceState.videoType = newVideoType;
165
                     oldSourceState.videoType = newVideoType;
166
+                    sourceChanged = true;
164
 
167
 
165
                     // Since having a mix of eps that do/don't support multi-stream in the same call is supported, emit
168
                     // Since having a mix of eps that do/don't support multi-stream in the same call is supported, emit
166
                     // SOURCE_VIDEO_TYPE_CHANGED event when the remote source changes videoType.
169
                     // SOURCE_VIDEO_TYPE_CHANGED event when the remote source changes videoType.
168
                         this.eventEmitter.emit(SignalingEvents.SOURCE_VIDEO_TYPE_CHANGED, sourceName, newVideoType);
171
                         this.eventEmitter.emit(SignalingEvents.SOURCE_VIDEO_TYPE_CHANGED, sourceName, newVideoType);
169
                     }
172
                     }
170
                 }
173
                 }
174
+
175
+                if (sourceChanged && FeatureFlags.isSsrcRewritingSupported()) {
176
+                    this.eventEmitter.emit(
177
+                        SignalingEvents.SOURCE_UPDATED,
178
+                        sourceName,
179
+                        mucNick,
180
+                        newMutedState,
181
+                        newVideoType);
182
+                }
171
             }
183
             }
172
 
184
 
173
             // Cleanup removed source names
185
             // Cleanup removed source names

+ 28
- 1
modules/xmpp/SignalingLayerImpl.spec.js View File

179
                     true
179
                     true
180
                 );
180
                 );
181
             });
181
             });
182
-            it('from a user with SourceInfo', () => {
182
+            it('from a user with SourceInfo and ssrc-rewriting disabled', () => {
183
                 const emitterSpy = spyOn(signalingLayer.eventEmitter, 'emit');
183
                 const emitterSpy = spyOn(signalingLayer.eventEmitter, 'emit');
184
                 const sourceInfo = {
184
                 const sourceInfo = {
185
                     '12345678-a0': {
185
                     '12345678-a0': {
203
                     true
203
                     true
204
                 );
204
                 );
205
             });
205
             });
206
+
207
+            it('from a user with sourceInfo and ssrc-rewriting enabled', () => {
208
+                FeatureFlags.init({ ssrcRewritingEnabled: true });
209
+                const emitterSpy = spyOn(signalingLayer.eventEmitter, 'emit');
210
+                const sourceInfo = {
211
+                    '12345678-a0': {
212
+                        muted: true
213
+                    }
214
+                };
215
+
216
+                chatRoom.mockSourceInfoPresence('endpoint1', sourceInfo);
217
+
218
+                // <audiomuted/> still included for backwards compat and ChatRoom will emit the presence event
219
+                chatRoom.emitPresenceListener({
220
+                    tagName: 'audiomuted',
221
+                    value: 'true'
222
+                }, 'endpoint1');
223
+
224
+                expect(emitterSpy).toHaveBeenCalledTimes(2);
225
+                expect(emitterSpy.calls.argsFor(1)).toEqual([
226
+                    SignalingEvents.SOURCE_UPDATED,
227
+                    '12345678-a0',
228
+                    'endpoint1',
229
+                    true,
230
+                    undefined
231
+                ]);
232
+            });
206
         });
233
         });
207
     });
234
     });
208
     describe('getPeerMediaInfo', () => {
235
     describe('getPeerMediaInfo', () => {

+ 2
- 0
service/RTC/SignalingEvents.spec.ts View File

7
         PEER_MUTED_CHANGED,
7
         PEER_MUTED_CHANGED,
8
         PEER_VIDEO_TYPE_CHANGED,
8
         PEER_VIDEO_TYPE_CHANGED,
9
         SOURCE_MUTED_CHANGED,
9
         SOURCE_MUTED_CHANGED,
10
+        SOURCE_UPDATED,
10
         SOURCE_VIDEO_TYPE_CHANGED,
11
         SOURCE_VIDEO_TYPE_CHANGED,
11
         SignalingEvents,
12
         SignalingEvents,
12
         ...others
13
         ...others
16
         expect( PEER_MUTED_CHANGED ).toBe( 'signaling.peerMuted' );
17
         expect( PEER_MUTED_CHANGED ).toBe( 'signaling.peerMuted' );
17
         expect( PEER_VIDEO_TYPE_CHANGED ).toBe( 'signaling.peerVideoType' );
18
         expect( PEER_VIDEO_TYPE_CHANGED ).toBe( 'signaling.peerVideoType' );
18
         expect( SOURCE_MUTED_CHANGED ).toBe( 'signaling.sourceMuted');
19
         expect( SOURCE_MUTED_CHANGED ).toBe( 'signaling.sourceMuted');
20
+        expect( SOURCE_UPDATED ).toBe( 'signaling.sourceUpdated' );
19
         expect( SOURCE_VIDEO_TYPE_CHANGED ).toBe( 'signaling.sourceVideoType');
21
         expect( SOURCE_VIDEO_TYPE_CHANGED ).toBe( 'signaling.sourceVideoType');
20
 
22
 
21
         expect( SignalingEvents ).toBeDefined();
23
         expect( SignalingEvents ).toBeDefined();

+ 11
- 0
service/RTC/SignalingEvents.ts View File

24
      */
24
      */
25
     SOURCE_MUTED_CHANGED = 'signaling.sourceMuted',
25
     SOURCE_MUTED_CHANGED = 'signaling.sourceMuted',
26
 
26
 
27
+    /**
28
+     * Event triggered when presence for a source is received.
29
+     *
30
+     * @param {string} sourceName - The name of the source.
31
+     * @param {string} endpointId - The endpoint id.
32
+     * @param {boolean} muted - The new muted state.
33
+     * @param {string} videoType - The video type of the source.
34
+     */
35
+    SOURCE_UPDATED = 'signaling.sourceUpdated',
36
+
27
     /**
37
     /**
28
      * Event triggered when source's video type changes.
38
      * Event triggered when source's video type changes.
29
      *
39
      *
37
 export const PEER_MUTED_CHANGED = SignalingEvents.PEER_MUTED_CHANGED;
47
 export const PEER_MUTED_CHANGED = SignalingEvents.PEER_MUTED_CHANGED;
38
 export const PEER_VIDEO_TYPE_CHANGED = SignalingEvents.PEER_VIDEO_TYPE_CHANGED;
48
 export const PEER_VIDEO_TYPE_CHANGED = SignalingEvents.PEER_VIDEO_TYPE_CHANGED;
39
 export const SOURCE_MUTED_CHANGED = SignalingEvents.SOURCE_MUTED_CHANGED;
49
 export const SOURCE_MUTED_CHANGED = SignalingEvents.SOURCE_MUTED_CHANGED;
50
+export const SOURCE_UPDATED = SignalingEvents.SOURCE_UPDATED;
40
 export const SOURCE_VIDEO_TYPE_CHANGED = SignalingEvents.SOURCE_VIDEO_TYPE_CHANGED;
51
 export const SOURCE_VIDEO_TYPE_CHANGED = SignalingEvents.SOURCE_VIDEO_TYPE_CHANGED;

Loading…
Cancel
Save