|
@@ -1,292 +0,0 @@
|
1
|
|
-/* global $ */
|
2
|
|
-var logger = require("jitsi-meet-logger").getLogger(__filename);
|
3
|
|
-
|
4
|
|
-
|
5
|
|
-/*
|
6
|
|
- Here we do modifications of local video SSRCs. There are 2 situations we have
|
7
|
|
- to handle:
|
8
|
|
-
|
9
|
|
- 1. We generate SSRC for local recvonly video stream. This is the case when we
|
10
|
|
- have no local camera and it is not generated automatically, but SSRC=1 is
|
11
|
|
- used implicitly. If that happens RTCP packets will be dropped by the JVB
|
12
|
|
- and we won't be able to request video key frames correctly.
|
13
|
|
-
|
14
|
|
- 2. A hack to re-use SSRC of the first video stream for any new stream created
|
15
|
|
- in future. It turned out that Chrome may keep on using the SSRC of removed
|
16
|
|
- video stream in RTCP even though a new one has been created. So we just
|
17
|
|
- want to avoid that by re-using it. Jingle 'source-remove'/'source-add'
|
18
|
|
- notifications are blocked once first video SSRC is advertised to the focus.
|
19
|
|
-
|
20
|
|
- What this hack does:
|
21
|
|
-
|
22
|
|
- 1. Stores the SSRC of the first video stream created by
|
23
|
|
- a) scanning Jingle session-accept/session-invite for existing video SSRC
|
24
|
|
- b) watching for 'source-add' for new video stream if it has not been
|
25
|
|
- created in step a)
|
26
|
|
- 2. Exposes method 'mungeLocalVideoSSRC' which replaces any new video SSRC with
|
27
|
|
- the stored one. It is called by 'TracablePeerConnection' before local SDP is
|
28
|
|
- returned to the other parts of the application.
|
29
|
|
- 3. Scans 'source-remove'/'source-add' notifications for stored video SSRC and
|
30
|
|
- blocks those notifications. This makes Jicofo and all participants think
|
31
|
|
- that it exists all the time even if the video stream has been removed or
|
32
|
|
- replaced locally. Thanks to that there is no additional signaling activity
|
33
|
|
- on video mute or when switching to the desktop stream.
|
34
|
|
- */
|
35
|
|
-
|
36
|
|
-var SDP = require('./SDP');
|
37
|
|
-var RandomUtil = require('../util/RandomUtil');
|
38
|
|
-var RTCBrowserType = require('../RTC/RTCBrowserType');
|
39
|
|
-
|
40
|
|
-/**
|
41
|
|
- * The hack is enabled on all browsers except FF by default
|
42
|
|
- * FIXME finish the hack once removeStream method is implemented in FF
|
43
|
|
- * @type {boolean}
|
44
|
|
- */
|
45
|
|
-var isEnabled = !RTCBrowserType.isFirefox();
|
46
|
|
-
|
47
|
|
-
|
48
|
|
-/**
|
49
|
|
- * Stored SSRC of local video stream.
|
50
|
|
- */
|
51
|
|
-var localVideoSSRC;
|
52
|
|
-
|
53
|
|
-/**
|
54
|
|
- * SSRC used for recvonly video stream when we have no local camera.
|
55
|
|
- * This is in order to tell Chrome what SSRC should be used in RTCP requests
|
56
|
|
- * instead of 1.
|
57
|
|
- */
|
58
|
|
-var localRecvOnlySSRC, localRecvOnlyMSID, localRecvOnlyMSLabel, localRecvOnlyLabel;
|
59
|
|
-
|
60
|
|
-/**
|
61
|
|
- * cname for <tt>localRecvOnlySSRC</tt>
|
62
|
|
- */
|
63
|
|
-var localRecvOnlyCName;
|
64
|
|
-
|
65
|
|
-/**
|
66
|
|
- * Method removes <source> element which describes <tt>localVideoSSRC</tt>
|
67
|
|
- * from given Jingle IQ.
|
68
|
|
- * @param modifyIq 'source-add' or 'source-remove' Jingle IQ.
|
69
|
|
- * @param actionName display name of the action which will be printed in log
|
70
|
|
- * messages.
|
71
|
|
- * @returns {*} modified Jingle IQ, so that it does not contain <source> element
|
72
|
|
- * corresponding to <tt>localVideoSSRC</tt> or <tt>null</tt> if no
|
73
|
|
- * other SSRCs left to be signaled after removing it.
|
74
|
|
- */
|
75
|
|
-var filterOutSource = function (modifyIq, actionName) {
|
76
|
|
- var modifyIqTree = $(modifyIq.tree());
|
77
|
|
-
|
78
|
|
- if (!localVideoSSRC)
|
79
|
|
- return modifyIqTree[0];
|
80
|
|
-
|
81
|
|
- var videoSSRC = modifyIqTree.find(
|
82
|
|
- '>jingle>content[name="video"]' +
|
83
|
|
- '>description>source[ssrc="' + localVideoSSRC + '"]');
|
84
|
|
-
|
85
|
|
- if (!videoSSRC.length) {
|
86
|
|
- return modifyIqTree[0];
|
87
|
|
- }
|
88
|
|
-
|
89
|
|
- logger.info(
|
90
|
|
- 'Blocking ' + actionName + ' for local video SSRC: ' + localVideoSSRC);
|
91
|
|
-
|
92
|
|
- videoSSRC.remove();
|
93
|
|
-
|
94
|
|
- // Check if any sources still left to be added/removed
|
95
|
|
- if (modifyIqTree.find('>jingle>content>description>source').length) {
|
96
|
|
- return modifyIqTree[0];
|
97
|
|
- } else {
|
98
|
|
- return null;
|
99
|
|
- }
|
100
|
|
-};
|
101
|
|
-
|
102
|
|
-/**
|
103
|
|
- * Scans given Jingle IQ for video SSRC and stores it.
|
104
|
|
- * @param jingleIq the Jingle IQ to be scanned for video SSRC.
|
105
|
|
- */
|
106
|
|
-var storeLocalVideoSSRC = function (jingleIq) {
|
107
|
|
- var videoSSRCs =
|
108
|
|
- $(jingleIq.tree())
|
109
|
|
- .find('>jingle>content[name="video"]>description>source');
|
110
|
|
-
|
111
|
|
- videoSSRCs.each(function (idx, ssrcElem) {
|
112
|
|
- if (localVideoSSRC)
|
113
|
|
- return;
|
114
|
|
- // We consider SSRC real only if it has msid attribute
|
115
|
|
- // recvonly streams in FF do not have it as well as local SSRCs
|
116
|
|
- // we generate for recvonly streams in Chrome
|
117
|
|
- var ssrSel = $(ssrcElem);
|
118
|
|
- var msid = ssrSel.find('>parameter[name="msid"]');
|
119
|
|
- if (msid.length) {
|
120
|
|
- var ssrcVal = ssrSel.attr('ssrc');
|
121
|
|
- if (ssrcVal) {
|
122
|
|
- localVideoSSRC = ssrcVal;
|
123
|
|
- logger.info('Stored local video SSRC' +
|
124
|
|
- ' for future re-use: ' + localVideoSSRC);
|
125
|
|
- }
|
126
|
|
- }
|
127
|
|
- });
|
128
|
|
-};
|
129
|
|
-
|
130
|
|
-/**
|
131
|
|
- * Generates new label/mslabel attribute
|
132
|
|
- * @returns {string} label/mslabel attribute
|
133
|
|
- */
|
134
|
|
-function generateLabel() {
|
135
|
|
- return RandomUtil.randomHexString(8) + "-" + RandomUtil.randomHexString(4) +
|
136
|
|
- "-" + RandomUtil.randomHexString(4) + "-" +
|
137
|
|
- RandomUtil.randomHexString(4) + "-" + RandomUtil.randomHexString(12);
|
138
|
|
-}
|
139
|
|
-
|
140
|
|
-/**
|
141
|
|
- * Generates new SSRC for local video recvonly stream.
|
142
|
|
- * FIXME what about eventual SSRC collision ?
|
143
|
|
- */
|
144
|
|
-function generateRecvonlySSRC() {
|
145
|
|
-
|
146
|
|
- localVideoSSRC = localRecvOnlySSRC =
|
147
|
|
- localVideoSSRC ?
|
148
|
|
- localVideoSSRC : Math.random().toString(10).substring(2, 11);
|
149
|
|
-
|
150
|
|
- localRecvOnlyCName =
|
151
|
|
- Math.random().toString(36).substring(2);
|
152
|
|
-
|
153
|
|
- localRecvOnlyMSLabel = generateLabel();
|
154
|
|
- localRecvOnlyLabel = generateLabel();
|
155
|
|
- localRecvOnlyMSID = localRecvOnlyMSLabel + " " + localRecvOnlyLabel;
|
156
|
|
-
|
157
|
|
- logger.info(
|
158
|
|
- "Generated local recvonly SSRC: " + localRecvOnlySSRC +
|
159
|
|
- ", cname: " + localRecvOnlyCName);
|
160
|
|
-}
|
161
|
|
-
|
162
|
|
-var LocalSSRCReplacement = {
|
163
|
|
- /**
|
164
|
|
- * Method must be called before 'session-initiate' or 'session-invite' is
|
165
|
|
- * sent. Scans the IQ for local video SSRC and stores it if detected.
|
166
|
|
- *
|
167
|
|
- * @param sessionInit our 'session-initiate' or 'session-accept' Jingle IQ
|
168
|
|
- * which will be scanned for local video SSRC.
|
169
|
|
- */
|
170
|
|
- processSessionInit: function (sessionInit) {
|
171
|
|
- if (!isEnabled)
|
172
|
|
- return;
|
173
|
|
-
|
174
|
|
- if (localVideoSSRC) {
|
175
|
|
- logger.error("Local SSRC stored already: " + localVideoSSRC);
|
176
|
|
- return;
|
177
|
|
- }
|
178
|
|
- storeLocalVideoSSRC(sessionInit);
|
179
|
|
- },
|
180
|
|
- /**
|
181
|
|
- * If we have local video SSRC stored searched given
|
182
|
|
- * <tt>localDescription</tt> for video SSRC and makes sure it is replaced
|
183
|
|
- * with the stored one.
|
184
|
|
- * @param localDescription local description object that will have local
|
185
|
|
- * video SSRC replaced with the stored one
|
186
|
|
- * @returns modified <tt>localDescription</tt> object.
|
187
|
|
- */
|
188
|
|
- mungeLocalVideoSSRC: function (localDescription) {
|
189
|
|
- if (!isEnabled)
|
190
|
|
- return localDescription;
|
191
|
|
-
|
192
|
|
- if (!localDescription) {
|
193
|
|
- logger.warn("localDescription is null or undefined");
|
194
|
|
- return localDescription;
|
195
|
|
- }
|
196
|
|
-
|
197
|
|
- // IF we have local video SSRC stored make sure it is replaced
|
198
|
|
- // with old SSRC
|
199
|
|
- var sdp = new SDP(localDescription.sdp);
|
200
|
|
- if (sdp.media.length < 2)
|
201
|
|
- return;
|
202
|
|
-
|
203
|
|
- if (localVideoSSRC && sdp.media[1].indexOf("a=ssrc:") !== -1 &&
|
204
|
|
- !sdp.containsSSRC(localVideoSSRC)) {
|
205
|
|
- // Get new video SSRC
|
206
|
|
- var map = sdp.getMediaSsrcMap();
|
207
|
|
- var videoPart = map[1];
|
208
|
|
- var videoSSRCs = videoPart.ssrcs;
|
209
|
|
- var newSSRC = Object.keys(videoSSRCs)[0];
|
210
|
|
-
|
211
|
|
- logger.info(
|
212
|
|
- "Replacing new video SSRC: " + newSSRC +
|
213
|
|
- " with " + localVideoSSRC);
|
214
|
|
-
|
215
|
|
- localDescription.sdp =
|
216
|
|
- sdp.raw.replace(
|
217
|
|
- new RegExp('a=ssrc:' + newSSRC, 'g'),
|
218
|
|
- 'a=ssrc:' + localVideoSSRC);
|
219
|
|
- }
|
220
|
|
- else if (sdp.media[1].indexOf('a=ssrc:') === -1 &&
|
221
|
|
- sdp.media[1].indexOf('a=recvonly') !== -1) {
|
222
|
|
- // Make sure we have any SSRC for recvonly video stream
|
223
|
|
- if (!localRecvOnlySSRC) {
|
224
|
|
- generateRecvonlySSRC();
|
225
|
|
- }
|
226
|
|
-
|
227
|
|
- logger.info('No SSRC in video recvonly stream' +
|
228
|
|
- ' - adding SSRC: ' + localRecvOnlySSRC);
|
229
|
|
-
|
230
|
|
- sdp.media[1] += 'a=ssrc:' + localRecvOnlySSRC +
|
231
|
|
- ' cname:' + localRecvOnlyCName + '\r\n' +
|
232
|
|
- 'a=ssrc:' + localRecvOnlySSRC +
|
233
|
|
- ' msid:' + localRecvOnlyMSID + '\r\n' +
|
234
|
|
- 'a=ssrc:' + localRecvOnlySSRC +
|
235
|
|
- ' mslabel:' + localRecvOnlyMSLabel + '\r\n' +
|
236
|
|
- 'a=ssrc:' + localRecvOnlySSRC +
|
237
|
|
- ' label:' + localRecvOnlyLabel + '\r\n';
|
238
|
|
-
|
239
|
|
- localDescription.sdp = sdp.session + sdp.media.join('');
|
240
|
|
- }
|
241
|
|
- return localDescription;
|
242
|
|
- },
|
243
|
|
- /**
|
244
|
|
- * Method must be called before 'source-add' notification is sent. In case
|
245
|
|
- * we have local video SSRC advertised already it will be removed from the
|
246
|
|
- * notification. If no other SSRCs are described by given IQ null will be
|
247
|
|
- * returned which means that there is no point in sending the notification.
|
248
|
|
- * @param sourceAdd 'source-add' Jingle IQ to be processed
|
249
|
|
- * @returns modified 'source-add' IQ which can be sent to the focus or
|
250
|
|
- * <tt>null</tt> if no notification shall be sent. It is no longer
|
251
|
|
- * a Strophe IQ Builder instance, but DOM element tree.
|
252
|
|
- */
|
253
|
|
- processSourceAdd: function (sourceAdd) {
|
254
|
|
- if (!isEnabled)
|
255
|
|
- return sourceAdd;
|
256
|
|
-
|
257
|
|
- if (!localVideoSSRC) {
|
258
|
|
- // Store local SSRC if available
|
259
|
|
- storeLocalVideoSSRC(sourceAdd);
|
260
|
|
- return sourceAdd;
|
261
|
|
- } else {
|
262
|
|
- return filterOutSource(sourceAdd, 'source-add');
|
263
|
|
- }
|
264
|
|
- },
|
265
|
|
- /**
|
266
|
|
- * Method must be called before 'source-remove' notification is sent.
|
267
|
|
- * Removes local video SSRC from the notification. If there are no other
|
268
|
|
- * SSRCs described in the given IQ <tt>null</tt> will be returned which
|
269
|
|
- * means that there is no point in sending the notification.
|
270
|
|
- * @param sourceRemove 'source-remove' Jingle IQ to be processed
|
271
|
|
- * @returns modified 'source-remove' IQ which can be sent to the focus or
|
272
|
|
- * <tt>null</tt> if no notification shall be sent. It is no longer
|
273
|
|
- * a Strophe IQ Builder instance, but DOM element tree.
|
274
|
|
- */
|
275
|
|
- processSourceRemove: function (sourceRemove) {
|
276
|
|
- if (!isEnabled)
|
277
|
|
- return sourceRemove;
|
278
|
|
-
|
279
|
|
- return filterOutSource(sourceRemove, 'source-remove');
|
280
|
|
- },
|
281
|
|
-
|
282
|
|
- /**
|
283
|
|
- * Turns the hack on or off
|
284
|
|
- * @param enabled <tt>true</tt> to enable the hack or <tt>false</tt>
|
285
|
|
- * to disable it
|
286
|
|
- */
|
287
|
|
- setEnabled: function (enabled) {
|
288
|
|
- isEnabled = enabled;
|
289
|
|
- }
|
290
|
|
-};
|
291
|
|
-
|
292
|
|
-module.exports = LocalSSRCReplacement;
|