|
|
@@ -10,6 +10,13 @@ const SIM_LAYER_1_RID = '1';
|
|
10
|
10
|
const SIM_LAYER_2_RID = '2';
|
|
11
|
11
|
const SIM_LAYER_3_RID = '3';
|
|
12
|
12
|
|
|
|
13
|
+const TransceiverDirection = {
|
|
|
14
|
+ INACTIVE: 'inactive',
|
|
|
15
|
+ RECVONLY: 'recvonly',
|
|
|
16
|
+ SENDONLY: 'sendonly',
|
|
|
17
|
+ SENDRECV: 'sendrecv'
|
|
|
18
|
+};
|
|
|
19
|
+
|
|
13
|
20
|
export const SIM_LAYER_RIDS = [ SIM_LAYER_1_RID, SIM_LAYER_2_RID, SIM_LAYER_3_RID ];
|
|
14
|
21
|
|
|
15
|
22
|
/**
|
|
|
@@ -63,6 +70,45 @@ export class TPCUtils {
|
|
63
|
70
|
];
|
|
64
|
71
|
}
|
|
65
|
72
|
|
|
|
73
|
+ /**
|
|
|
74
|
+ * Returns the transceiver associated with a given RTCRtpSender/RTCRtpReceiver.
|
|
|
75
|
+ *
|
|
|
76
|
+ * @param {string} mediaType - type of track associated with the transceiver 'audio' or 'video'.
|
|
|
77
|
+ * @param {JitsiLocalTrack} localTrack - local track to be used for lookup.
|
|
|
78
|
+ * @returns {RTCRtpTransceiver}
|
|
|
79
|
+ */
|
|
|
80
|
+ _findTransceiver(mediaType, localTrack = null) {
|
|
|
81
|
+ let transceiver = null;
|
|
|
82
|
+
|
|
|
83
|
+ if (localTrack) {
|
|
|
84
|
+ transceiver = this.pc.peerconnection.getTransceivers()
|
|
|
85
|
+ .find(t => t.sender?.track?.id === localTrack.getTrackId());
|
|
|
86
|
+ } else if (mediaType) {
|
|
|
87
|
+ transceiver = this.pc.peerconnection.getTransceivers()
|
|
|
88
|
+ .find(t => t.receiver?.track?.kind === mediaType);
|
|
|
89
|
+ }
|
|
|
90
|
+
|
|
|
91
|
+ return transceiver;
|
|
|
92
|
+ }
|
|
|
93
|
+
|
|
|
94
|
+ /**
|
|
|
95
|
+ * Obtains stream encodings that need to be configured on the given track based
|
|
|
96
|
+ * on the track media type and the simulcast setting.
|
|
|
97
|
+ * @param {JitsiLocalTrack} localTrack
|
|
|
98
|
+ */
|
|
|
99
|
+ _getStreamEncodings(localTrack) {
|
|
|
100
|
+ if (this.pc.isSimulcastOn() && localTrack.isVideoTrack()) {
|
|
|
101
|
+ return this.localStreamEncodingsConfig;
|
|
|
102
|
+ }
|
|
|
103
|
+
|
|
|
104
|
+ return localTrack.isVideoTrack()
|
|
|
105
|
+ ? [ {
|
|
|
106
|
+ active: true,
|
|
|
107
|
+ maxBitrate: this.videoBitrates.high
|
|
|
108
|
+ } ]
|
|
|
109
|
+ : [ { active: true } ];
|
|
|
110
|
+ }
|
|
|
111
|
+
|
|
66
|
112
|
/**
|
|
67
|
113
|
* Ensures that the ssrcs associated with a FID ssrc-group appear in the correct order, i.e.,
|
|
68
|
114
|
* the primary ssrc first and the secondary rtx ssrc later. This is important for unified
|
|
|
@@ -75,7 +121,7 @@ export class TPCUtils {
|
|
75
|
121
|
const parsedSdp = transform.parse(description.sdp);
|
|
76
|
122
|
|
|
77
|
123
|
parsedSdp.media.forEach(mLine => {
|
|
78
|
|
- if (mLine.type === 'audio') {
|
|
|
124
|
+ if (mLine.type === MediaType.AUDIO) {
|
|
79
|
125
|
return;
|
|
80
|
126
|
}
|
|
81
|
127
|
if (!mLine.ssrcGroups || !mLine.ssrcGroups.length) {
|
|
|
@@ -97,24 +143,6 @@ export class TPCUtils {
|
|
97
|
143
|
});
|
|
98
|
144
|
}
|
|
99
|
145
|
|
|
100
|
|
- /**
|
|
101
|
|
- * Obtains stream encodings that need to be configured on the given track based
|
|
102
|
|
- * on the track media type and the simulcast setting.
|
|
103
|
|
- * @param {JitsiLocalTrack} localTrack
|
|
104
|
|
- */
|
|
105
|
|
- _getStreamEncodings(localTrack) {
|
|
106
|
|
- if (this.pc.isSimulcastOn() && localTrack.isVideoTrack()) {
|
|
107
|
|
- return this.localStreamEncodingsConfig;
|
|
108
|
|
- }
|
|
109
|
|
-
|
|
110
|
|
- return localTrack.isVideoTrack()
|
|
111
|
|
- ? [ {
|
|
112
|
|
- active: true,
|
|
113
|
|
- maxBitrate: this.videoBitrates.high
|
|
114
|
|
- } ]
|
|
115
|
|
- : [ { active: true } ];
|
|
116
|
|
- }
|
|
117
|
|
-
|
|
118
|
146
|
/**
|
|
119
|
147
|
* Takes in a *unified plan* offer and inserts the appropriate
|
|
120
|
148
|
* parameters for adding simulcast receive support.
|
|
|
@@ -126,20 +154,19 @@ export class TPCUtils {
|
|
126
|
154
|
* with its sdp field modified to advertise simulcast receive support
|
|
127
|
155
|
*/
|
|
128
|
156
|
insertUnifiedPlanSimulcastReceive(desc) {
|
|
129
|
|
- // a=simulcast line is not needed on browsers where
|
|
130
|
|
- // we munge SDP for turning on simulcast. Remove this check
|
|
131
|
|
- // when we move to RID/MID based simulcast on all browsers.
|
|
|
157
|
+ // a=simulcast line is not needed on browsers where we SDP munging is used for enabling on simulcast.
|
|
|
158
|
+ // Remove this check when the client switches to RID/MID based simulcast on all browsers.
|
|
132
|
159
|
if (browser.usesSdpMungingForSimulcast()) {
|
|
133
|
160
|
return desc;
|
|
134
|
161
|
}
|
|
135
|
162
|
const sdp = transform.parse(desc.sdp);
|
|
136
|
|
- const idx = sdp.media.findIndex(mline => mline.type === 'video');
|
|
|
163
|
+ const idx = sdp.media.findIndex(mline => mline.type === MediaType.VIDEO);
|
|
137
|
164
|
|
|
138
|
165
|
if (sdp.media[idx].rids && (sdp.media[idx].simulcast_03 || sdp.media[idx].simulcast)) {
|
|
139
|
166
|
// Make sure we don't have the simulcast recv line on video descriptions other than
|
|
140
|
167
|
// the first video description.
|
|
141
|
168
|
sdp.media.forEach((mline, i) => {
|
|
142
|
|
- if (mline.type === 'video' && i !== idx) {
|
|
|
169
|
+ if (mline.type === MediaType.VIDEO && i !== idx) {
|
|
143
|
170
|
sdp.media[i].rids = undefined;
|
|
144
|
171
|
sdp.media[i].simulcast = undefined;
|
|
145
|
172
|
|
|
|
@@ -201,7 +228,7 @@ export class TPCUtils {
|
|
201
|
228
|
// Use pc.addTransceiver() for the initiator case when local tracks are getting added
|
|
202
|
229
|
// to the peerconnection before a session-initiate is sent over to the peer.
|
|
203
|
230
|
const transceiverInit = {
|
|
204
|
|
- direction: 'sendrecv',
|
|
|
231
|
+ direction: TransceiverDirection.SENDRECV,
|
|
205
|
232
|
streams: [ localTrack.getOriginalStream() ],
|
|
206
|
233
|
sendEncodings: []
|
|
207
|
234
|
};
|
|
|
@@ -226,39 +253,13 @@ export class TPCUtils {
|
|
226
|
253
|
addTrackUnmute(localTrack) {
|
|
227
|
254
|
const mediaType = localTrack.getType();
|
|
228
|
255
|
const track = localTrack.getTrack();
|
|
229
|
|
-
|
|
230
|
|
- // The assumption here is that the first transceiver of the specified
|
|
231
|
|
- // media type is that of the local track.
|
|
232
|
|
- const transceiver = this.pc.peerconnection.getTransceivers()
|
|
233
|
|
- .find(t => t.receiver && t.receiver.track && t.receiver.track.kind === mediaType);
|
|
|
256
|
+ const transceiver = this._findTransceiver(mediaType);
|
|
234
|
257
|
|
|
235
|
258
|
if (!transceiver) {
|
|
236
|
259
|
return Promise.reject(new Error(`RTCRtpTransceiver for ${mediaType} not found`));
|
|
237
|
260
|
}
|
|
238
|
261
|
logger.debug(`Adding ${localTrack} on ${this.pc}`);
|
|
239
|
262
|
|
|
240
|
|
- // If the client starts with audio/video muted setting, the transceiver direction will be set to 'recvonly'.
|
|
241
|
|
- if (transceiver.direction === 'recvonly') {
|
|
242
|
|
- const stream = localTrack.getOriginalStream();
|
|
243
|
|
-
|
|
244
|
|
- if (stream && track) {
|
|
245
|
|
- try {
|
|
246
|
|
- this.pc.peerconnection.addTrack(track, stream);
|
|
247
|
|
- } catch (error) {
|
|
248
|
|
- logger.error(`Adding ${localTrack} failed on ${this.pc}:${error?.message}`);
|
|
249
|
|
-
|
|
250
|
|
- return Promise.reject(error);
|
|
251
|
|
- }
|
|
252
|
|
-
|
|
253
|
|
- return this.setEncodings(localTrack).then(() => {
|
|
254
|
|
- this.pc.localTracks.set(localTrack.rtcId, localTrack);
|
|
255
|
|
- transceiver.direction = 'sendrecv';
|
|
256
|
|
- });
|
|
257
|
|
- }
|
|
258
|
|
-
|
|
259
|
|
- return Promise.resolve();
|
|
260
|
|
- }
|
|
261
|
|
-
|
|
262
|
263
|
return transceiver.sender.replaceTrack(track);
|
|
263
|
264
|
}
|
|
264
|
265
|
|
|
|
@@ -295,8 +296,7 @@ export class TPCUtils {
|
|
295
|
296
|
*/
|
|
296
|
297
|
removeTrackMute(localTrack) {
|
|
297
|
298
|
const mediaType = localTrack.getType();
|
|
298
|
|
- const transceiver = this.pc.peerconnection.getTransceivers()
|
|
299
|
|
- .find(t => t.sender && t.sender.track && t.sender.track.id === localTrack.getTrackId());
|
|
|
299
|
+ const transceiver = this._findTransceiver(mediaType, localTrack);
|
|
300
|
300
|
|
|
301
|
301
|
if (!transceiver) {
|
|
302
|
302
|
return Promise.reject(new Error(`RTCRtpTransceiver for ${mediaType} not found`));
|
|
|
@@ -316,7 +316,7 @@ export class TPCUtils {
|
|
316
|
316
|
replaceTrack(oldTrack, newTrack) {
|
|
317
|
317
|
if (oldTrack && newTrack) {
|
|
318
|
318
|
const mediaType = newTrack.getType();
|
|
319
|
|
- const stream = newTrack.getOriginalStream();
|
|
|
319
|
+ const stream = newTrack.stream;
|
|
320
|
320
|
|
|
321
|
321
|
// Ignore cases when the track is replaced while the device is in a muted state,like
|
|
322
|
322
|
// replacing camera when video muted or replacing mic when audio muted. These JitsiLocalTracks
|
|
|
@@ -328,11 +328,11 @@ export class TPCUtils {
|
|
328
|
328
|
|
|
329
|
329
|
return Promise.resolve();
|
|
330
|
330
|
}
|
|
331
|
|
- const track = mediaType === MediaType.AUDIO
|
|
332
|
|
- ? stream.getAudioTracks()[0]
|
|
333
|
|
- : stream.getVideoTracks()[0];
|
|
334
|
|
- const transceiver = this.pc.peerconnection.getTransceivers()
|
|
335
|
|
- .find(t => t.receiver.track.kind === mediaType && !t.stopped);
|
|
|
331
|
+
|
|
|
332
|
+ const transceiver = oldTrack.isVideoTrack() && browser.doesVideoMuteByStreamRemove && oldTrack.isMuted()
|
|
|
333
|
+ ? this._findTransceiver(mediaType)
|
|
|
334
|
+ : this._findTransceiver(mediaType, oldTrack);
|
|
|
335
|
+ const track = mediaType === MediaType.AUDIO ? stream.getAudioTracks()[0] : stream.getVideoTracks()[0];
|
|
336
|
336
|
|
|
337
|
337
|
if (!transceiver) {
|
|
338
|
338
|
return Promise.reject(new Error('replace track failed'));
|
|
|
@@ -357,6 +357,16 @@ export class TPCUtils {
|
|
357
|
357
|
} else if (oldTrack && !newTrack) {
|
|
358
|
358
|
return this.removeTrackMute(oldTrack)
|
|
359
|
359
|
.then(() => {
|
|
|
360
|
+ const mediaType = oldTrack.getType();
|
|
|
361
|
+ const transceiver = this._findTransceiver(mediaType);
|
|
|
362
|
+
|
|
|
363
|
+ // Change the direction on the transceiver to 'recvonly' so that a 'removetrack'
|
|
|
364
|
+ // is fired on the associated media stream on the remote peer.
|
|
|
365
|
+ if (transceiver) {
|
|
|
366
|
+ transceiver.direction = TransceiverDirection.RECVONLY;
|
|
|
367
|
+ }
|
|
|
368
|
+
|
|
|
369
|
+ // Remove the old track from the list of local tracks.
|
|
360
|
370
|
this.pc.localTracks.delete(oldTrack.rtcId);
|
|
361
|
371
|
this.pc.localSSRCs.delete(oldTrack.rtcId);
|
|
362
|
372
|
});
|
|
|
@@ -364,7 +374,18 @@ export class TPCUtils {
|
|
364
|
374
|
const ssrc = this.pc.localSSRCs.get(newTrack.rtcId);
|
|
365
|
375
|
|
|
366
|
376
|
return this.addTrackUnmute(newTrack)
|
|
|
377
|
+ .then(() => this.setEncodings(newTrack))
|
|
367
|
378
|
.then(() => {
|
|
|
379
|
+ const mediaType = newTrack.getType();
|
|
|
380
|
+ const transceiver = this._findTransceiver(mediaType, newTrack);
|
|
|
381
|
+
|
|
|
382
|
+ // Change the direction on the transceiver back to 'sendrecv' so that a 'track'
|
|
|
383
|
+ // event is fired on the remote peer.
|
|
|
384
|
+ if (transceiver) {
|
|
|
385
|
+ transceiver.direction = TransceiverDirection.SENDRECV;
|
|
|
386
|
+ }
|
|
|
387
|
+
|
|
|
388
|
+ // Add the new track to the list of local tracks.
|
|
368
|
389
|
this.pc.localTracks.set(newTrack.rtcId, newTrack);
|
|
369
|
390
|
this.pc.localSSRCs.set(newTrack.rtcId, ssrc);
|
|
370
|
391
|
});
|
|
|
@@ -395,9 +416,9 @@ export class TPCUtils {
|
|
395
|
416
|
* @returns {Promise<void>} - resolved when done.
|
|
396
|
417
|
*/
|
|
397
|
418
|
setEncodings(track) {
|
|
398
|
|
- const transceiver = this.pc.peerconnection.getTransceivers()
|
|
399
|
|
- .find(t => t.sender && t.sender.track && t.sender.track.kind === track.getType());
|
|
400
|
|
- const parameters = transceiver.sender.getParameters();
|
|
|
419
|
+ const mediaType = track.getType();
|
|
|
420
|
+ const transceiver = this._findTransceiver(mediaType, track);
|
|
|
421
|
+ const parameters = transceiver?.sender?.getParameters();
|
|
401
|
422
|
|
|
402
|
423
|
// Resolve if the encodings are not available yet. This happens immediately after the track is added to the
|
|
403
|
424
|
// peerconnection on chrome in unified-plan. It is ok to ignore and not report the error here since the
|
|
|
@@ -428,12 +449,12 @@ export class TPCUtils {
|
|
428
|
449
|
if (active) {
|
|
429
|
450
|
// The first transceiver is for the local track and only this one can be set to 'sendrecv'
|
|
430
|
451
|
if (idx === 0 && localTracks.length) {
|
|
431
|
|
- transceiver.direction = 'sendrecv';
|
|
|
452
|
+ transceiver.direction = TransceiverDirection.SENDRECV;
|
|
432
|
453
|
} else {
|
|
433
|
|
- transceiver.direction = 'recvonly';
|
|
|
454
|
+ transceiver.direction = TransceiverDirection.RECVONLY;
|
|
434
|
455
|
}
|
|
435
|
456
|
} else {
|
|
436
|
|
- transceiver.direction = 'inactive';
|
|
|
457
|
+ transceiver.direction = TransceiverDirection.INACTIVE;
|
|
437
|
458
|
}
|
|
438
|
459
|
});
|
|
439
|
460
|
}
|