瀏覽代碼

fix(SignalingLayer) Update SSRC owners on leave. (#2184)

* fix(SignalingLayer) Update SSRC owners on leave.
Update the SSRC owners in the following cases:
1. When a remote endpoint leaves the call.
2. When a source-remove is received.
3. When a source is remapped (with ssrc-rewriting enabled).
Create the remote track even if presence is not yet received. The ssrc owner check prevents the client from creating a dummy track when the call switches over from p2p to jvb when the last remote endpoint leaves the call.

* ref(SignalingLayer) alpha sort methods.
Clean up unused methods, _findEndpointSourceInfoForMediaType is not used anymore.

* squash: Address review comments.
dev1
Jaya Allamsetty 2 年之前
父節點
當前提交
4d6873c2f4
沒有連結到貢獻者的電子郵件帳戶。

+ 3
- 0
JitsiConference.js 查看文件

1910
 
1910
 
1911
             remoteTracks && (tracksToBeRemoved = [ ...tracksToBeRemoved, ...remoteTracks ]);
1911
             remoteTracks && (tracksToBeRemoved = [ ...tracksToBeRemoved, ...remoteTracks ]);
1912
 
1912
 
1913
+            // Update the SSRC owners list.
1914
+            session._signalingLayer.updateSsrcOwnersOnLeave(id);
1915
+
1913
             // Remove the ssrcs from the remote description and renegotiate.
1916
             // Remove the ssrcs from the remote description and renegotiate.
1914
             session.removeRemoteStreamsOnLeave(id);
1917
             session.removeRemoteStreamsOnLeave(id);
1915
         }
1918
         }

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

958
                 + 'track creation failed!'));
958
                 + 'track creation failed!'));
959
 
959
 
960
         return;
960
         return;
961
-    } else if (!ownerEndpointId) {
961
+    }
962
+
963
+    if (!ownerEndpointId) {
962
         GlobalOnErrorHandler.callErrorHandler(
964
         GlobalOnErrorHandler.callErrorHandler(
963
             new Error(`No SSRC owner known for remote stream[ssrc=${trackSsrc},id=${streamId},type=${mediaType}]`
965
             new Error(`No SSRC owner known for remote stream[ssrc=${trackSsrc},id=${streamId},type=${mediaType}]`
964
             + 'track creation failed!'));
966
             + 'track creation failed!'));
969
     const sourceName = this.signalingLayer.getTrackSourceName(trackSsrc);
971
     const sourceName = this.signalingLayer.getTrackSourceName(trackSsrc);
970
     const peerMediaInfo = this.signalingLayer.getPeerMediaInfo(ownerEndpointId, mediaType, sourceName);
972
     const peerMediaInfo = this.signalingLayer.getPeerMediaInfo(ownerEndpointId, mediaType, sourceName);
971
 
973
 
972
-    if (!peerMediaInfo) {
973
-        GlobalOnErrorHandler.callErrorHandler(
974
-            new Error(`${this}: no sourceInfo available for ${ownerEndpointId}:${sourceName} track creation failed!`));
974
+    // Assume default presence state for remote source. Presence can be received after source signaling. This shouldn't
975
+    // prevent the endpoint from creating a remote track for the source.
976
+    let muted = true;
977
+    let videoType = VideoType.CAMERA;
975
 
978
 
976
-        return;
979
+    if (peerMediaInfo) {
980
+        muted = peerMediaInfo.muted;
981
+        videoType = peerMediaInfo.videoType; // can be undefined
982
+    } else {
983
+        logger.info(`${this}: no source-info available for ${ownerEndpointId}:${sourceName}, assuming default state`);
977
     }
984
     }
978
 
985
 
979
-    const muted = peerMediaInfo.muted;
980
-    const videoType = peerMediaInfo.videoType; // can be undefined
981
-
982
     this._createRemoteTrack(ownerEndpointId, stream, track, mediaType, videoType, trackSsrc, muted, sourceName);
986
     this._createRemoteTrack(ownerEndpointId, stream, track, mediaType, videoType, trackSsrc, muted, sourceName);
983
 };
987
 };
984
 
988
 
1082
     const streamId = stream.id;
1086
     const streamId = stream.id;
1083
     const trackId = track?.id;
1087
     const trackId = track?.id;
1084
 
1088
 
1089
+    // Ignore stream removed events for JVB "mixed" sources (used for RTCP termination).
1085
     if (!RTC.isUserStreamById(streamId)) {
1090
     if (!RTC.isUserStreamById(streamId)) {
1086
-        logger.info(`${this} ignored remote 'stream removed' event for non-user stream[id=${streamId}]`);
1087
-
1088
         return;
1091
         return;
1089
     }
1092
     }
1090
 
1093
 
1101
     }
1104
     }
1102
 
1105
 
1103
     const toBeRemoved = this.getRemoteTracks().find(
1106
     const toBeRemoved = this.getRemoteTracks().find(
1104
-        remoteTrack => remoteTrack.getStreamId() === streamId
1105
-        && remoteTrack.getTrackId() === trackId);
1107
+        remoteTrack => remoteTrack.getStreamId() === streamId && remoteTrack.getTrackId() === trackId);
1106
 
1108
 
1107
     if (!toBeRemoved) {
1109
     if (!toBeRemoved) {
1108
         GlobalOnErrorHandler.callErrorHandler(new Error(`${this} remote track removal failed - track not found`));
1110
         GlobalOnErrorHandler.callErrorHandler(new Error(`${this} remote track removal failed - track not found`));
1110
         return;
1112
         return;
1111
     }
1113
     }
1112
 
1114
 
1113
-    logger.info(`${this} remote track removed stream[id=${streamId},trackId=${trackId}]`);
1114
     this._removeRemoteTrack(toBeRemoved);
1115
     this._removeRemoteTrack(toBeRemoved);
1115
 };
1116
 };
1116
 
1117
 
1141
  * @returns {void}
1142
  * @returns {void}
1142
  */
1143
  */
1143
 TraceablePeerConnection.prototype._removeRemoteTrack = function(toBeRemoved) {
1144
 TraceablePeerConnection.prototype._removeRemoteTrack = function(toBeRemoved) {
1145
+    logger.info(`${this} Removing remote track stream[id=${toBeRemoved.getStreamId()},`
1146
+        + `trackId=${toBeRemoved.getTrackId()}]`);
1147
+
1144
     toBeRemoved.dispose();
1148
     toBeRemoved.dispose();
1145
     const participantId = toBeRemoved.getParticipantId();
1149
     const participantId = toBeRemoved.getParticipantId();
1146
     const userTracksByMediaType = this.remoteTracks.get(participantId);
1150
     const userTracksByMediaType = this.remoteTracks.get(participantId);

+ 39
- 13
modules/proxyconnection/CustomSignalingLayer.js 查看文件

2
 
2
 
3
 import SignalingLayer from '../../service/RTC/SignalingLayer';
3
 import SignalingLayer from '../../service/RTC/SignalingLayer';
4
 
4
 
5
-
6
 const logger = getLogger(__filename);
5
 const logger = getLogger(__filename);
7
 
6
 
8
-
9
 /**
7
 /**
10
  * Custom semi-mock implementation for the Proxy connection service.
8
  * Custom semi-mock implementation for the Proxy connection service.
11
  */
9
  */
29
         this.chatRoom = null;
27
         this.chatRoom = null;
30
     }
28
     }
31
 
29
 
32
-    /**
33
-     * Sets the <tt>ChatRoom</tt> instance used.
34
-     * @param {ChatRoom} room
35
-     */
36
-    setChatRoom(room) {
37
-        this.chatRoom = room;
38
-    }
39
-
40
     /**
30
     /**
41
      * @inheritDoc
31
      * @inheritDoc
42
      */
32
      */
58
         return this.ssrcOwners.get(ssrc);
48
         return this.ssrcOwners.get(ssrc);
59
     }
49
     }
60
 
50
 
51
+    /**
52
+     * @inheritDoc
53
+     */
54
+    getTrackSourceName(ssrc) { // eslint-disable-line no-unused-vars
55
+        return undefined;
56
+    }
57
+
58
+    /**
59
+     * @inheritDoc
60
+     */
61
+    removeSSRCOwners(ssrcList) {
62
+        if (!ssrcList?.length) {
63
+            return;
64
+        }
65
+
66
+        for (const ssrc of ssrcList) {
67
+            this.ssrcOwners.delete(ssrc);
68
+        }
69
+    }
70
+
71
+    /**
72
+     * Sets the <tt>ChatRoom</tt> instance used.
73
+     * @param {ChatRoom} room
74
+     */
75
+    setChatRoom(room) {
76
+        this.chatRoom = room;
77
+    }
78
+
61
     /**
79
     /**
62
      * @inheritDoc
80
      * @inheritDoc
63
      */
81
      */
93
     /**
111
     /**
94
      * @inheritDoc
112
      * @inheritDoc
95
      */
113
      */
96
-    getTrackSourceName(ssrc) { // eslint-disable-line no-unused-vars
97
-        return undefined;
114
+    setTrackSourceName(ssrc, sourceName) { // eslint-disable-line no-unused-vars
98
     }
115
     }
99
 
116
 
100
     /**
117
     /**
101
      * @inheritDoc
118
      * @inheritDoc
102
      */
119
      */
103
-    setTrackSourceName(ssrc, sourceName) { // eslint-disable-line no-unused-vars
120
+    updateSsrcOwnersOnLeave(id) {
121
+        const ssrcs = Array.from(this.ssrcOwners)
122
+            .filter(entry => entry[1] === id)
123
+            .map(entry => entry[0]);
124
+
125
+        if (!ssrcs?.length) {
126
+            return;
127
+        }
128
+
129
+        this.removeSSRCOwners(ssrcs);
104
     }
130
     }
105
 }
131
 }

+ 10
- 0
modules/xmpp/JingleSessionPC.js 查看文件

1810
                 if (track) {
1810
                 if (track) {
1811
                     logger.debug(`Existing SSRC ${s.ssrc}: new owner ${s.owner}. name=${s.source}`);
1811
                     logger.debug(`Existing SSRC ${s.ssrc}: new owner ${s.owner}. name=${s.source}`);
1812
 
1812
 
1813
+                    // Update the SSRC owner.
1814
+                    this._signalingLayer.setSSRCOwner(s.ssrc, s.owner);
1815
+
1813
                     if (s.videoType === 'CAMERA') {
1816
                     if (s.videoType === 'CAMERA') {
1814
                         track._setVideoType('camera');
1817
                         track._setVideoType('camera');
1815
                     } else if (s.videoType === 'DESKTOP') {
1818
                     } else if (s.videoType === 'DESKTOP') {
2042
         const remoteSdp = this.usesUnifiedPlan
2045
         const remoteSdp = this.usesUnifiedPlan
2043
             ? new SDP(this.peerconnection.peerconnection.remoteDescription.sdp)
2046
             ? new SDP(this.peerconnection.peerconnection.remoteDescription.sdp)
2044
             : new SDP(this.peerconnection.remoteDescription.sdp);
2047
             : new SDP(this.peerconnection.remoteDescription.sdp);
2048
+        let ssrcs;
2045
 
2049
 
2046
         removeSsrcInfo.forEach((lines, idx) => {
2050
         removeSsrcInfo.forEach((lines, idx) => {
2047
             // eslint-disable-next-line no-param-reassign
2051
             // eslint-disable-next-line no-param-reassign
2048
             lines = lines.split('\r\n');
2052
             lines = lines.split('\r\n');
2049
             lines.pop(); // remove empty last element;
2053
             lines.pop(); // remove empty last element;
2054
+            ssrcs = lines.map(line => Number(line.split('a=ssrc:')[1]?.split(' ')[0]));
2055
+
2050
             if (this.usesUnifiedPlan) {
2056
             if (this.usesUnifiedPlan) {
2051
                 let mid;
2057
                 let mid;
2052
 
2058
 
2085
                 });
2091
                 });
2086
             }
2092
             }
2087
         });
2093
         });
2094
+
2095
+        // Update the ssrc owners list.
2096
+        ssrcs?.length && this._signalingLayer.removeSSRCOwners(ssrcs);
2097
+
2088
         remoteSdp.raw = remoteSdp.session + remoteSdp.media.join('');
2098
         remoteSdp.raw = remoteSdp.session + remoteSdp.media.join('');
2089
 
2099
 
2090
         return remoteSdp;
2100
         return remoteSdp;

+ 95
- 76
modules/xmpp/SignalingLayerImpl.js 查看文件

6
 import SignalingLayer, { getMediaTypeFromSourceName } from '../../service/RTC/SignalingLayer';
6
 import SignalingLayer, { getMediaTypeFromSourceName } from '../../service/RTC/SignalingLayer';
7
 import { VideoType } from '../../service/RTC/VideoType';
7
 import { VideoType } from '../../service/RTC/VideoType';
8
 import { XMPPEvents } from '../../service/xmpp/XMPPEvents';
8
 import { XMPPEvents } from '../../service/xmpp/XMPPEvents';
9
+import FeatureFlags from '../flags/FeatureFlags';
9
 
10
 
10
 import { filterNodeFromPresenceJSON } from './ChatRoom';
11
 import { filterNodeFromPresenceJSON } from './ChatRoom';
11
 
12
 
79
         return false;
80
         return false;
80
     }
81
     }
81
 
82
 
82
-    /**
83
-     * Check is given endpoint has advertised <SourceInfo/> in it's presence which means that the source name signaling
84
-     * is used by this endpoint.
85
-     *
86
-     * @param {EndpointId} endpointId
87
-     * @returns {boolean}
88
-     */
89
-    _doesEndpointSendNewSourceInfo(endpointId) {
90
-        const presence = this.chatRoom?.getLastPresence(endpointId);
91
-
92
-        return Boolean(presence && presence.find(node => node.tagName === SOURCE_INFO_PRESENCE_ELEMENT));
93
-    }
94
-
95
-    /**
96
-     * Sets the <tt>ChatRoom</tt> instance used and binds presence listeners.
97
-     * @param {ChatRoom} room
98
-     */
99
-    setChatRoom(room) {
100
-        const oldChatRoom = this.chatRoom;
101
-
102
-        this.chatRoom = room;
103
-        if (oldChatRoom) {
104
-            oldChatRoom.removePresenceListener(
105
-                'audiomuted', this._audioMuteHandler);
106
-            oldChatRoom.removePresenceListener(
107
-                'videomuted', this._videoMuteHandler);
108
-            oldChatRoom.removePresenceListener(
109
-                'videoType', this._videoTypeHandler);
110
-            this._sourceInfoHandler
111
-                && oldChatRoom.removePresenceListener(SOURCE_INFO_PRESENCE_ELEMENT, this._sourceInfoHandler);
112
-            this._memberLeftHandler
113
-                && oldChatRoom.removeEventListener(XMPPEvents.MUC_MEMBER_LEFT, this._memberLeftHandler);
114
-        }
115
-        if (room) {
116
-            this._bindChatRoomEventHandlers(room);
117
-            this._addLocalSourceInfoToPresence();
118
-        }
119
-    }
120
-
121
     /**
83
     /**
122
      * Binds event listeners to the chat room instance.
84
      * Binds event listeners to the chat room instance.
123
      * @param {ChatRoom} room
85
      * @param {ChatRoom} room
235
     }
197
     }
236
 
198
 
237
     /**
199
     /**
238
-     * Finds the first source of given media type for the given endpoint.
239
-     * @param endpointId
240
-     * @param mediaType
241
-     * @returns {SourceInfo|null}
242
-     * @private
200
+     * Check is given endpoint has advertised <SourceInfo/> in it's presence which means that the source name signaling
201
+     * is used by this endpoint.
202
+     *
203
+     * @param {EndpointId} endpointId
204
+     * @returns {boolean}
243
      */
205
      */
244
-    _findEndpointSourceInfoForMediaType(endpointId, mediaType) {
245
-        const remoteSourceState = this._remoteSourceState[endpointId];
246
-
247
-        if (!remoteSourceState) {
248
-            return null;
249
-        }
206
+    _doesEndpointSendNewSourceInfo(endpointId) {
207
+        const presence = this.chatRoom?.getLastPresence(endpointId);
250
 
208
 
251
-        for (const sourceInfo of Object.values(remoteSourceState)) {
252
-            const _mediaType = getMediaTypeFromSourceName(sourceInfo.sourceName);
209
+        return Boolean(presence && presence.find(node => node.tagName === SOURCE_INFO_PRESENCE_ELEMENT));
210
+    }
253
 
211
 
254
-            if (_mediaType === mediaType) {
255
-                return sourceInfo;
256
-            }
212
+    /**
213
+     * Logs a debug or error message to console depending on whether SSRC rewriting is enabled or not.
214
+     * Owner changes are permitted only when SSRC rewriting is enabled.
215
+     *
216
+     * @param {string} message - The message to be logged.
217
+     * @returns {void}
218
+     */
219
+    _logOwnerChangedMessage(message) {
220
+        if (FeatureFlags.isSsrcRewritingSupported()) {
221
+            logger.debug(message);
222
+        } else {
223
+            logger.error(message);
257
         }
224
         }
258
-
259
-        return null;
260
     }
225
     }
261
 
226
 
262
     /**
227
     /**
322
         return this.ssrcOwners.get(ssrc);
287
         return this.ssrcOwners.get(ssrc);
323
     }
288
     }
324
 
289
 
290
+    /**
291
+     * @inheritDoc
292
+     */
293
+    getTrackSourceName(ssrc) {
294
+        return this._sourceNames.get(ssrc);
295
+    }
296
+
297
+    /**
298
+     * @inheritDoc
299
+     */
300
+    removeSSRCOwners(ssrcList) {
301
+        if (!ssrcList?.length) {
302
+            return;
303
+        }
304
+
305
+        for (const ssrc of ssrcList) {
306
+            this.ssrcOwners.delete(ssrc);
307
+        }
308
+    }
309
+
310
+    /**
311
+     * Sets the <tt>ChatRoom</tt> instance used and binds presence listeners.
312
+     * @param {ChatRoom} room
313
+     */
314
+    setChatRoom(room) {
315
+        const oldChatRoom = this.chatRoom;
316
+
317
+        this.chatRoom = room;
318
+        if (oldChatRoom) {
319
+            oldChatRoom.removePresenceListener(
320
+                'audiomuted', this._audioMuteHandler);
321
+            oldChatRoom.removePresenceListener(
322
+                'videomuted', this._videoMuteHandler);
323
+            oldChatRoom.removePresenceListener(
324
+                'videoType', this._videoTypeHandler);
325
+            this._sourceInfoHandler
326
+                && oldChatRoom.removePresenceListener(SOURCE_INFO_PRESENCE_ELEMENT, this._sourceInfoHandler);
327
+            this._memberLeftHandler
328
+                && oldChatRoom.removeEventListener(XMPPEvents.MUC_MEMBER_LEFT, this._memberLeftHandler);
329
+        }
330
+        if (room) {
331
+            this._bindChatRoomEventHandlers(room);
332
+            this._addLocalSourceInfoToPresence();
333
+        }
334
+    }
335
+
325
     /**
336
     /**
326
      * @inheritDoc
337
      * @inheritDoc
327
      */
338
      */
335
         const existingOwner = this.ssrcOwners.get(ssrc);
346
         const existingOwner = this.ssrcOwners.get(ssrc);
336
 
347
 
337
         if (existingOwner && existingOwner !== endpointId) {
348
         if (existingOwner && existingOwner !== endpointId) {
338
-            logger.error(`SSRC owner re-assigned from ${existingOwner} to ${endpointId}`);
349
+            this._logOwnerChangedMessage(`SSRC owner re-assigned from ${existingOwner} to ${endpointId}`);
339
         }
350
         }
340
         this.ssrcOwners.set(ssrc, endpointId);
351
         this.ssrcOwners.set(ssrc, endpointId);
341
     }
352
     }
357
         return false;
368
         return false;
358
     }
369
     }
359
 
370
 
371
+    /**
372
+     * @inheritDoc
373
+     */
374
+    setTrackSourceName(ssrc, sourceName) {
375
+        if (typeof ssrc !== 'number') {
376
+            throw new TypeError(`SSRC(${ssrc}) must be a number`);
377
+        }
378
+
379
+        // Now signaling layer instance is shared between different JingleSessionPC instances, so although very unlikely
380
+        // an SSRC conflict could potentially occur. Log a message to make debugging easier.
381
+        const existingName = this._sourceNames.get(ssrc);
382
+
383
+        if (existingName && existingName !== sourceName) {
384
+            this._logOwnerChangedMessage(`SSRC(${ssrc}) sourceName re-assigned from ${existingName} to ${sourceName}`);
385
+        }
386
+
387
+        this._sourceNames.set(ssrc, sourceName);
388
+    }
389
+
360
     /**
390
     /**
361
      * @inheritDoc
391
      * @inheritDoc
362
      */
392
      */
378
     /**
408
     /**
379
      * @inheritDoc
409
      * @inheritDoc
380
      */
410
      */
381
-    getTrackSourceName(ssrc) {
382
-        return this._sourceNames.get(ssrc);
383
-    }
384
-
385
-    /**
386
-     * @inheritDoc
387
-     */
388
-    setTrackSourceName(ssrc, sourceName) {
389
-        if (typeof ssrc !== 'number') {
390
-            throw new TypeError(`SSRC(${ssrc}) must be a number`);
391
-        }
392
-
393
-        // Now signaling layer instance is shared between different JingleSessionPC instances, so although very unlikely
394
-        // an SSRC conflict could potentially occur. Log a message to make debugging easier.
395
-        const existingName = this._sourceNames.get(ssrc);
411
+    updateSsrcOwnersOnLeave(id) {
412
+        const ssrcs = Array.from(this.ssrcOwners)
413
+            .filter(entry => entry[1] === id)
414
+            .map(entry => entry[0]);
396
 
415
 
397
-        if (existingName && existingName !== sourceName) {
398
-            logger.error(`SSRC(${ssrc}) sourceName re-assigned from ${existingName} to ${sourceName}`);
416
+        if (!ssrcs?.length) {
417
+            return;
399
         }
418
         }
400
 
419
 
401
-        this._sourceNames.set(ssrc, sourceName);
420
+        this.removeSSRCOwners(ssrcs);
402
     }
421
     }
403
 
422
 
404
 }
423
 }

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

88
  * @interface SignalingLayer
88
  * @interface SignalingLayer
89
  */
89
  */
90
 export default class SignalingLayer extends Listenable {
90
 export default class SignalingLayer extends Listenable {
91
-
92
-    /**
93
-     * Obtains the endpoint ID for given SSRC.
94
-     * @param {number} ssrc the SSRC number.
95
-     * @return {string|null} the endpoint ID for given media SSRC.
96
-     */
97
-    getSSRCOwner(ssrc) { // eslint-disable-line no-unused-vars
98
-        throw new Error('not implemented');
99
-    }
100
-
101
     /**
91
     /**
102
      * Obtains the info about given media advertised in the MUC presence of
92
      * Obtains the info about given media advertised in the MUC presence of
103
      * the participant identified by the given MUC JID.
93
      * the participant identified by the given MUC JID.
126
         throw new Error('not implemented');
116
         throw new Error('not implemented');
127
     }
117
     }
128
 
118
 
119
+    /**
120
+     * Obtains the endpoint ID for given SSRC.
121
+     * @param {number} ssrc the SSRC number.
122
+     * @return {string|null} the endpoint ID for given media SSRC.
123
+     */
124
+    getSSRCOwner(ssrc) { // eslint-disable-line no-unused-vars
125
+        throw new Error('not implemented');
126
+    }
127
+
129
     /**
128
     /**
130
      * Obtains the source name for given SSRC.
129
      * Obtains the source name for given SSRC.
131
      * @param {number} ssrc the track's SSRC identifier.
130
      * @param {number} ssrc the track's SSRC identifier.
135
         throw new Error('not implemented');
134
         throw new Error('not implemented');
136
     }
135
     }
137
 
136
 
137
+    /**
138
+     * Removes the association between a given SSRC and its current owner so that it can re-used when the SSRC gets
139
+     * remapped to another source from a different endpoint.
140
+     * @param {number} ssrc a list of SSRCs.
141
+     */
142
+    removeSSRCOwners(ssrcList) { // eslint-disable-line no-unused-vars
143
+    }
144
+
138
     /**
145
     /**
139
      * Set an SSRC owner.
146
      * Set an SSRC owner.
140
      * @param {number} ssrc an SSRC to be owned
147
      * @param {number} ssrc an SSRC to be owned
144
     setSSRCOwner(ssrc, endpointId) { // eslint-disable-line no-unused-vars
151
     setSSRCOwner(ssrc, endpointId) { // eslint-disable-line no-unused-vars
145
     }
152
     }
146
 
153
 
147
-
148
     /**
154
     /**
149
      * Adjusts muted status of given track.
155
      * Adjusts muted status of given track.
150
      *
156
      *
172
      */
178
      */
173
     setTrackVideoType(sourceName, videoType) { // eslint-disable-line no-unused-vars
179
     setTrackVideoType(sourceName, videoType) { // eslint-disable-line no-unused-vars
174
     }
180
     }
181
+
182
+    /**
183
+     * Removes the SSRCs associated with a given endpoint from the SSRC owners.
184
+     *
185
+     * @param {string} id endpoint id of the participant leaving the call.
186
+     * @returns {void}
187
+     */
188
+    updateSsrcOwnersOnLeave(id) { // eslint-disable-line no-unused-vars
189
+    }
175
 }
190
 }

+ 2
- 0
types/hand-crafted/modules/xmpp/SignalingLayerImpl.d.ts 查看文件

7
   setChatRoom: ( room: ChatRoom ) => void;
7
   setChatRoom: ( room: ChatRoom ) => void;
8
   getPeerMediaInfo: ( owner: string, mediaType: MediaType ) => PeerMediaInfo | null;
8
   getPeerMediaInfo: ( owner: string, mediaType: MediaType ) => PeerMediaInfo | null;
9
   getSSRCOwner: ( ssrc: number ) => string | null;
9
   getSSRCOwner: ( ssrc: number ) => string | null;
10
+  removeSSRCOwners: (ssrcList: Array<number> ) => void;
10
   setSSRCOwner: ( ssrc: number, endpointId: string ) => void;
11
   setSSRCOwner: ( ssrc: number, endpointId: string ) => void;
12
+  updateSsrcOwnersOnLeave: ( id: string ) => void;
11
 }
13
 }

Loading…
取消
儲存