Browse Source

Feat ssrc rewriting (#2192)

tags/v0.0.2
Jaya Allamsetty 1 year ago
parent
commit
5713cb0d6d
No account linked to committer's email address

+ 31
- 15
JitsiConference.js View File

@@ -53,7 +53,8 @@ import BridgeVideoType from './service/RTC/BridgeVideoType';
53 53
 import CodecMimeType from './service/RTC/CodecMimeType';
54 54
 import { MediaType } from './service/RTC/MediaType';
55 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 58
 import { VideoType } from './service/RTC/VideoType';
58 59
 import {
59 60
     ACTION_JINGLE_RESTART,
@@ -156,7 +157,12 @@ export default function JitsiConference(options) {
156 157
     this.eventEmitter = new EventEmitter();
157 158
     this.options = options;
158 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 168
      * The signaling layer instance.
@@ -389,6 +395,17 @@ JitsiConference.prototype._init = function(options = {}) {
389 395
     );
390 396
 
391 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 410
     // Connection interrupted/restored listeners
394 411
     this._onIceConnectionInterrupted
@@ -1198,11 +1215,11 @@ JitsiConference.prototype._fireMuteChangeEvent = function(track) {
1198 1215
     if (this.mutedByFocusActor && track.isAudioTrack()) {
1199 1216
         const actorId = Strophe.getResourceFromJid(this.mutedByFocusActor);
1200 1217
 
1201
-        actorParticipant = this.participants[actorId];
1218
+        actorParticipant = this.participants.get(actorId);
1202 1219
     } else if (this.mutedVideoByFocusActor && track.isVideoTrack()) {
1203 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 1225
     // Send the video type message to the bridge if the track is not removed/added to the pc as part of
@@ -1631,11 +1648,10 @@ JitsiConference.prototype.setLastN = function(lastN) {
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 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,7 +1677,7 @@ JitsiConference.prototype.getParticipantCount = function(countHidden = false) {
1661 1677
  * @param id the id of the participant.
1662 1678
  */
1663 1679
 JitsiConference.prototype.getParticipantById = function(id) {
1664
-    return this.participants[id];
1680
+    return this.participants.get(id);
1665 1681
 };
1666 1682
 
1667 1683
 /**
@@ -1809,7 +1825,7 @@ JitsiConference.prototype.onMemberJoined = function(
1809 1825
         participant._tracks.push(track);
1810 1826
     }
1811 1827
 
1812
-    this.participants[id] = participant;
1828
+    this.participants.set(id, participant);
1813 1829
     this.eventEmitter.emit(
1814 1830
         JitsiConferenceEvents.USER_JOINED,
1815 1831
         id,
@@ -1922,10 +1938,10 @@ JitsiConference.prototype.onMemberLeft = function(jid, reason) {
1922 1938
         });
1923 1939
     }
1924 1940
 
1925
-    const participant = this.participants[id];
1941
+    const participant = this.participants.get(id);
1926 1942
 
1927 1943
     if (participant) {
1928
-        delete this.participants[id];
1944
+        this.participants.delete(id);
1929 1945
         this.eventEmitter.emit(JitsiConferenceEvents.USER_LEFT, id, participant, reason);
1930 1946
     }
1931 1947
 
@@ -1962,7 +1978,7 @@ JitsiConference.prototype.onMemberKicked = function(
1962 1978
         return;
1963 1979
     }
1964 1980
 
1965
-    const actorParticipant = this.participants[actorId];
1981
+    const actorParticipant = this.participants.get(actorId);
1966 1982
 
1967 1983
     if (isSelfPresence) {
1968 1984
         this.eventEmitter.emit(
@@ -1973,7 +1989,7 @@ JitsiConference.prototype.onMemberKicked = function(
1973 1989
         return;
1974 1990
     }
1975 1991
 
1976
-    const kickedParticipant = this.participants[kickedParticipantId];
1992
+    const kickedParticipant = this.participants.get(kickedParticipantId);
1977 1993
 
1978 1994
     kickedParticipant.setIsReplaced(isReplaceParticipant);
1979 1995
 
@@ -2999,7 +3015,7 @@ JitsiConference.prototype._acceptP2PIncomingCall = function(jingleSession, jingl
2999 3015
 
3000 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 3020
     if (participant) {
3005 3021
         remoteID = participant.getStatsID() || remoteID;
@@ -3367,7 +3383,7 @@ JitsiConference.prototype._startP2PSession = function(remoteJid) {
3367 3383
 
3368 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 3388
     if (participant) {
3373 3389
         remoteID = participant.getStatsID() || remoteID;

+ 6
- 6
JitsiConferenceEventManager.js View File

@@ -574,15 +574,15 @@ JitsiConferenceEventManager.prototype.setupRTCListeners = function() {
574 574
     });
575 575
 
576 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 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 588
     rtc.addListener(RTCEvents.ENDPOINT_MESSAGE_RECEIVED,

+ 2
- 0
JitsiConferenceEvents.spec.ts View File

@@ -31,6 +31,7 @@ describe( "/JitsiConferenceEvents members", () => {
31 31
         JVB121_STATUS,
32 32
         KICKED,
33 33
         PARTICIPANT_KICKED,
34
+        PARTICIPANT_SOURCE_UPDATED,
34 35
         LAST_N_ENDPOINTS_CHANGED,
35 36
         FORWARDED_SOURCES_CHANGED,
36 37
         LOCK_STATE_CHANGED,
@@ -108,6 +109,7 @@ describe( "/JitsiConferenceEvents members", () => {
108 109
         expect( JVB121_STATUS ).toBe( 'conference.jvb121Status' );
109 110
         expect( KICKED ).toBe( 'conference.kicked' );
110 111
         expect( PARTICIPANT_KICKED ).toBe( 'conference.participant_kicked' );
112
+        expect( PARTICIPANT_SOURCE_UPDATED ).toBe( 'conference.participant_source_updated' );
111 113
         expect( LAST_N_ENDPOINTS_CHANGED ).toBe( 'conference.lastNEndpointsChanged' );
112 114
         expect( FORWARDED_SOURCES_CHANGED ).toBe( 'conference.forwardedSourcesChanged' );
113 115
         expect( LOCK_STATE_CHANGED ).toBe( 'conference.lock_state_changed' );

+ 6
- 0
JitsiConferenceEvents.ts View File

@@ -241,6 +241,11 @@ export enum JitsiConferenceEvents {
241 241
      */
242 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 250
      * Indicates that the conference has switched between JVB and P2P connections.
246 251
      * The first argument of this event is a <tt>boolean</tt> which when set to
@@ -490,6 +495,7 @@ export const E2EE_VERIFICATION_COMPLETED = JitsiConferenceEvents.E2EE_VERIFICATI
490 495
 export const JVB121_STATUS = JitsiConferenceEvents.JVB121_STATUS;
491 496
 export const KICKED = JitsiConferenceEvents.KICKED;
492 497
 export const PARTICIPANT_KICKED = JitsiConferenceEvents.PARTICIPANT_KICKED;
498
+export const PARTICIPANT_SOURCE_UPDATED = JitsiConferenceEvents.PARTICIPANT_SOURCE_UPDATED;
493 499
 export const LAST_N_ENDPOINTS_CHANGED = JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED;
494 500
 export const FORWARDED_SOURCES_CHANGED = JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED;
495 501
 export const LOCK_STATE_CHANGED = JitsiConferenceEvents.LOCK_STATE_CHANGED;

+ 47
- 0
JitsiParticipant.js View File

@@ -43,6 +43,19 @@ export default class JitsiParticipant {
43 43
         this._isReplacing = isReplacing;
44 44
         this._isReplaced = isReplaced;
45 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,6 +74,32 @@ export default class JitsiParticipant {
61 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 104
      * Returns the bot type for the participant.
66 105
      *
@@ -130,6 +169,14 @@ export default class JitsiParticipant {
130 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 181
      * @returns {String} The stats ID of this participant.
135 182
      */

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

@@ -6,6 +6,7 @@ import { JitsiTrackEvents } from '../../JitsiTrackEvents';
6 6
 import * as CodecMimeType from '../../service/RTC/CodecMimeType';
7 7
 import { MediaDirection } from '../../service/RTC/MediaDirection';
8 8
 import { MediaType } from '../../service/RTC/MediaType';
9
+import { VideoType } from '../../service/RTC/VideoType';
9 10
 import {
10 11
     ICE_DURATION,
11 12
     ICE_STATE_CHANGED
@@ -1793,115 +1794,88 @@ export default class JingleSessionPC extends JingleSession {
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 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 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 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 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 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 1836
             let node = $build('content', {
1842 1837
                 xmlns: 'urn:xmpp:jingle:1',
1843
-                name: 'video'
1838
+                name: mediaType
1844 1839
             }).c('description', {
1845 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 1878
             node = node.up();
1904
-
1905 1879
             this._addOrRemoveRemoteStream(true /* add */, node.node);
1906 1880
         }
1907 1881
     }

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

@@ -142,12 +142,14 @@ export default class SignalingLayerImpl extends SignalingLayer {
142 142
                 = this._remoteSourceState[endpointId] || (this._remoteSourceState[endpointId] = {});
143 143
 
144 144
             for (const sourceName of Object.keys(sourceInfoJSON)) {
145
+                let sourceChanged = false;
145 146
                 const mediaType = getMediaTypeFromSourceName(sourceName);
146 147
                 const newMutedState = Boolean(sourceInfoJSON[sourceName].muted);
147 148
                 const oldSourceState = endpointSourceState[sourceName]
148 149
                     || (endpointSourceState[sourceName] = { sourceName });
149 150
 
150 151
                 if (oldSourceState.muted !== newMutedState) {
152
+                    sourceChanged = true;
151 153
                     oldSourceState.muted = newMutedState;
152 154
                     if (emitEventsFromHere && !this._localSourceState[sourceName]) {
153 155
                         this.eventEmitter.emit(SignalingEvents.SOURCE_MUTED_CHANGED, sourceName, newMutedState);
@@ -161,6 +163,7 @@ export default class SignalingLayerImpl extends SignalingLayer {
161 163
 
162 164
                 if (oldSourceState.videoType !== newVideoType) {
163 165
                     oldSourceState.videoType = newVideoType;
166
+                    sourceChanged = true;
164 167
 
165 168
                     // Since having a mix of eps that do/don't support multi-stream in the same call is supported, emit
166 169
                     // SOURCE_VIDEO_TYPE_CHANGED event when the remote source changes videoType.
@@ -168,6 +171,15 @@ export default class SignalingLayerImpl extends SignalingLayer {
168 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 185
             // Cleanup removed source names

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

@@ -179,7 +179,7 @@ describe('SignalingLayerImpl', () => {
179 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 183
                 const emitterSpy = spyOn(signalingLayer.eventEmitter, 'emit');
184 184
                 const sourceInfo = {
185 185
                     '12345678-a0': {
@@ -203,6 +203,33 @@ describe('SignalingLayerImpl', () => {
203 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 235
     describe('getPeerMediaInfo', () => {

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

@@ -7,6 +7,7 @@ describe( "/service/RTC/SignalingEvents members", () => {
7 7
         PEER_MUTED_CHANGED,
8 8
         PEER_VIDEO_TYPE_CHANGED,
9 9
         SOURCE_MUTED_CHANGED,
10
+        SOURCE_UPDATED,
10 11
         SOURCE_VIDEO_TYPE_CHANGED,
11 12
         SignalingEvents,
12 13
         ...others
@@ -16,6 +17,7 @@ describe( "/service/RTC/SignalingEvents members", () => {
16 17
         expect( PEER_MUTED_CHANGED ).toBe( 'signaling.peerMuted' );
17 18
         expect( PEER_VIDEO_TYPE_CHANGED ).toBe( 'signaling.peerVideoType' );
18 19
         expect( SOURCE_MUTED_CHANGED ).toBe( 'signaling.sourceMuted');
20
+        expect( SOURCE_UPDATED ).toBe( 'signaling.sourceUpdated' );
19 21
         expect( SOURCE_VIDEO_TYPE_CHANGED ).toBe( 'signaling.sourceVideoType');
20 22
 
21 23
         expect( SignalingEvents ).toBeDefined();

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

@@ -24,6 +24,16 @@ export enum SignalingEvents {
24 24
      */
25 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 38
      * Event triggered when source's video type changes.
29 39
      *
@@ -37,4 +47,5 @@ export enum SignalingEvents {
37 47
 export const PEER_MUTED_CHANGED = SignalingEvents.PEER_MUTED_CHANGED;
38 48
 export const PEER_VIDEO_TYPE_CHANGED = SignalingEvents.PEER_VIDEO_TYPE_CHANGED;
39 49
 export const SOURCE_MUTED_CHANGED = SignalingEvents.SOURCE_MUTED_CHANGED;
50
+export const SOURCE_UPDATED = SignalingEvents.SOURCE_UPDATED;
40 51
 export const SOURCE_VIDEO_TYPE_CHANGED = SignalingEvents.SOURCE_VIDEO_TYPE_CHANGED;

Loading…
Cancel
Save