|
|
@@ -31,9 +31,6 @@ import { TPCUtils } from './TPCUtils';
|
|
31
|
31
|
const logger = getLogger(__filename);
|
|
32
|
32
|
const DEGRADATION_PREFERENCE_CAMERA = 'maintain-framerate';
|
|
33
|
33
|
const DEGRADATION_PREFERENCE_DESKTOP = 'maintain-resolution';
|
|
34
|
|
-const DD_HEADER_EXT_URI
|
|
35
|
|
- = 'https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension';
|
|
36
|
|
-const DD_HEADER_EXT_ID = 11;
|
|
37
|
34
|
|
|
38
|
35
|
/* eslint-disable max-params */
|
|
39
|
36
|
|
|
|
@@ -245,7 +242,11 @@ export default function TraceablePeerConnection(
|
|
245
|
242
|
|
|
246
|
243
|
this.peerconnection = new RTCPeerConnection(pcConfig, safeConstraints);
|
|
247
|
244
|
|
|
248
|
|
- this.tpcUtils = new TPCUtils(this);
|
|
|
245
|
+ this.tpcUtils = new TPCUtils(this, {
|
|
|
246
|
+ audioQuality: options.audioQuality,
|
|
|
247
|
+ isP2P: this.isP2P,
|
|
|
248
|
+ videoQuality: options.videoQuality
|
|
|
249
|
+ });
|
|
249
|
250
|
this.updateLog = [];
|
|
250
|
251
|
this.stats = {};
|
|
251
|
252
|
this.statsinterval = null;
|
|
|
@@ -311,19 +312,12 @@ export default function TraceablePeerConnection(
|
|
311
|
312
|
*/
|
|
312
|
313
|
this._senderMaxHeights = new Map();
|
|
313
|
314
|
|
|
314
|
|
- /**
|
|
315
|
|
- * Flag indicating bridge support for AV1 codec. On the bridge connection, it is supported only when support for
|
|
316
|
|
- * Dependency Descriptor header extensions is offered by Jicofo. H.264 simulcast is also possible when these
|
|
317
|
|
- * header extensions are negotiated.
|
|
318
|
|
- */
|
|
319
|
|
- this._supportsDDHeaderExt = false;
|
|
320
|
|
-
|
|
321
|
315
|
/**
|
|
322
|
316
|
* Holds the RTCRtpTransceiver mids that the local tracks are attached to, mapped per their
|
|
323
|
317
|
* {@link JitsiLocalTrack.rtcId}.
|
|
324
|
318
|
* @type {Map<string, string>}
|
|
325
|
319
|
*/
|
|
326
|
|
- this._localTrackTransceiverMids = new Map();
|
|
|
320
|
+ this.localTrackTransceiverMids = new Map();
|
|
327
|
321
|
|
|
328
|
322
|
/**
|
|
329
|
323
|
* Holds the SSRC map for the local tracks mapped by their source names.
|
|
|
@@ -505,20 +499,14 @@ TraceablePeerConnection.prototype.getConnectionState = function() {
|
|
505
|
499
|
* Obtains the media direction for given {@link MediaType} that needs to be set on a p2p peerconnection's remote SDP
|
|
506
|
500
|
* after a source-add or source-remove action. The method takes into account whether or not there are any
|
|
507
|
501
|
* local tracks for the given media type.
|
|
508
|
|
- * @param {MediaType} mediaType
|
|
|
502
|
+ * @param {MediaType} mediaType - The media type for which the direction is to be calculated.
|
|
509
|
503
|
* @param {boolean} isAddOperation whether the direction is to be calculated after a source-add action.
|
|
510
|
504
|
* @return {string} one of the SDP direction constants ('sendrecv, 'recvonly' etc.) which should be used when setting
|
|
511
|
505
|
* local description on the peerconnection.
|
|
512
|
506
|
* @private
|
|
513
|
507
|
*/
|
|
514
|
508
|
TraceablePeerConnection.prototype.getDesiredMediaDirection = function(mediaType, isAddOperation = false) {
|
|
515
|
|
- const hasLocalSource = this.hasAnyTracksOfType(mediaType);
|
|
516
|
|
-
|
|
517
|
|
- if (isAddOperation) {
|
|
518
|
|
- return hasLocalSource ? MediaDirection.SENDRECV : MediaDirection.SENDONLY;
|
|
519
|
|
- }
|
|
520
|
|
-
|
|
521
|
|
- return hasLocalSource ? MediaDirection.RECVONLY : MediaDirection.INACTIVE;
|
|
|
509
|
+ return this.tpcUtils.getDesiredMediaDirection(mediaType, isAddOperation);
|
|
522
|
510
|
};
|
|
523
|
511
|
|
|
524
|
512
|
/**
|
|
|
@@ -675,7 +663,7 @@ TraceablePeerConnection.prototype.getAudioLevels = function(speakerList = []) {
|
|
675
|
663
|
* @returns {boolean}
|
|
676
|
664
|
*/
|
|
677
|
665
|
TraceablePeerConnection.prototype.doesTrueSimulcast = function(localTrack) {
|
|
678
|
|
- const currentCodec = this.getConfiguredVideoCodec(localTrack);
|
|
|
666
|
+ const currentCodec = this.tpcUtils.getConfiguredVideoCodec(localTrack);
|
|
679
|
667
|
|
|
680
|
668
|
return this.isSpatialScalabilityOn() && this.tpcUtils.isRunningInSimulcastMode(currentCodec);
|
|
681
|
669
|
};
|
|
|
@@ -723,21 +711,6 @@ TraceablePeerConnection.prototype.getLocalVideoTracks = function() {
|
|
723
|
711
|
return this.getLocalTracks(MediaType.VIDEO);
|
|
724
|
712
|
};
|
|
725
|
713
|
|
|
726
|
|
-/**
|
|
727
|
|
- * Checks whether or not this {@link TraceablePeerConnection} instance contains any local tracks for given
|
|
728
|
|
- * <tt>mediaType</tt>.
|
|
729
|
|
- *
|
|
730
|
|
- * @param {MediaType} mediaType - The media type.
|
|
731
|
|
- * @return {boolean}
|
|
732
|
|
- */
|
|
733
|
|
-TraceablePeerConnection.prototype.hasAnyTracksOfType = function(mediaType) {
|
|
734
|
|
- if (!mediaType) {
|
|
735
|
|
- throw new Error('"mediaType" is required');
|
|
736
|
|
- }
|
|
737
|
|
-
|
|
738
|
|
- return this.getLocalTracks(mediaType).length > 0;
|
|
739
|
|
-};
|
|
740
|
|
-
|
|
741
|
714
|
/**
|
|
742
|
715
|
* Obtains all remote tracks currently known to this PeerConnection instance.
|
|
743
|
716
|
*
|
|
|
@@ -822,7 +795,7 @@ TraceablePeerConnection.prototype.getRemoteSourceInfoByParticipant = function(id
|
|
822
|
795
|
* @returns {Object}
|
|
823
|
796
|
*/
|
|
824
|
797
|
TraceablePeerConnection.prototype.getTargetVideoBitrates = function(localTrack) {
|
|
825
|
|
- const currentCodec = this.getConfiguredVideoCodec(localTrack);
|
|
|
798
|
+ const currentCodec = this.tpcUtils.getConfiguredVideoCodec(localTrack);
|
|
826
|
799
|
|
|
827
|
800
|
return this.tpcUtils.codecSettings[currentCodec].maxBitratesVideo;
|
|
828
|
801
|
};
|
|
|
@@ -1352,112 +1325,6 @@ TraceablePeerConnection.prototype._isSharingScreen = function() {
|
|
1352
|
1325
|
return Boolean(tracks.find(track => track.videoType === VideoType.DESKTOP));
|
|
1353
|
1326
|
};
|
|
1354
|
1327
|
|
|
1355
|
|
-/**
|
|
1356
|
|
- * Munges the order of the codecs in the SDP passed based on the preference
|
|
1357
|
|
- * set through config.js settings. All instances of the specified codec are
|
|
1358
|
|
- * moved up to the top of the list when it is preferred. The specified codec
|
|
1359
|
|
- * is deleted from the list if the configuration specifies that the codec be
|
|
1360
|
|
- * disabled.
|
|
1361
|
|
- * @param {RTCSessionDescription} description that needs to be munged.
|
|
1362
|
|
- * @returns {RTCSessionDescription} the munged description.
|
|
1363
|
|
- */
|
|
1364
|
|
-TraceablePeerConnection.prototype._mungeCodecOrder = function(description) {
|
|
1365
|
|
- if (!this.codecSettings) {
|
|
1366
|
|
- return description;
|
|
1367
|
|
- }
|
|
1368
|
|
-
|
|
1369
|
|
- const parsedSdp = transform.parse(description.sdp);
|
|
1370
|
|
- const mLines = parsedSdp.media.filter(m => m.type === this.codecSettings.mediaType);
|
|
1371
|
|
-
|
|
1372
|
|
- if (!mLines.length) {
|
|
1373
|
|
- return description;
|
|
1374
|
|
- }
|
|
1375
|
|
-
|
|
1376
|
|
- for (const mLine of mLines) {
|
|
1377
|
|
- const currentCodecs = this.getConfiguredVideoCodecs(description);
|
|
1378
|
|
-
|
|
1379
|
|
- for (const codec of currentCodecs) {
|
|
1380
|
|
- if (this.isP2P) {
|
|
1381
|
|
- // 1. Strip the high profile H264 codecs on all clients. macOS started offering encoder for H.264 level
|
|
1382
|
|
- // 5.2 but a decoder only for level 3.1. Therfore, strip all main and high level codecs for H.264.
|
|
1383
|
|
- // 2. There are multiple VP9 payload types generated by the browser, more payload types are added if the
|
|
1384
|
|
- // endpoint doesn't have a local video source. Therefore, strip all the high profile codec variants
|
|
1385
|
|
- // for VP9 so that only one payload type for VP9 is negotiated between the peers.
|
|
1386
|
|
- if (codec === CodecMimeType.H264 || codec === CodecMimeType.VP9) {
|
|
1387
|
|
- SDPUtil.stripCodec(mLine, codec, true /* high profile */);
|
|
1388
|
|
- }
|
|
1389
|
|
-
|
|
1390
|
|
- // Do not negotiate ULPFEC and RED either.
|
|
1391
|
|
- if (codec === CodecMimeType.ULPFEC || codec === CodecMimeType.RED) {
|
|
1392
|
|
- SDPUtil.stripCodec(mLine, codec, false);
|
|
1393
|
|
- }
|
|
1394
|
|
- }
|
|
1395
|
|
- }
|
|
1396
|
|
-
|
|
1397
|
|
- // Reorder the codecs based on the preferred settings.
|
|
1398
|
|
- if (!this.usesCodecSelectionAPI()) {
|
|
1399
|
|
- for (const codec of this.codecSettings.codecList.slice().reverse()) {
|
|
1400
|
|
- SDPUtil.preferCodec(mLine, codec, this.isP2P);
|
|
1401
|
|
- }
|
|
1402
|
|
- }
|
|
1403
|
|
- }
|
|
1404
|
|
-
|
|
1405
|
|
- return new RTCSessionDescription({
|
|
1406
|
|
- type: description.type,
|
|
1407
|
|
- sdp: transform.write(parsedSdp)
|
|
1408
|
|
- });
|
|
1409
|
|
-};
|
|
1410
|
|
-
|
|
1411
|
|
-/**
|
|
1412
|
|
- * Checks if the AV1 Dependency descriptors are negotiated on the bridge peerconnection and disables them when the
|
|
1413
|
|
- * codec selected is VP8 or VP9.
|
|
1414
|
|
- *
|
|
1415
|
|
- * @param {RTCSessionDescription} description that needs to be munged.
|
|
1416
|
|
- * @returns {RTCSessionDescription} the munged description.
|
|
1417
|
|
- */
|
|
1418
|
|
-TraceablePeerConnection.prototype._updateAv1DdHeaders = function(description) {
|
|
1419
|
|
- const parsedSdp = transform.parse(description.sdp);
|
|
1420
|
|
- const mLines = parsedSdp.media.filter(m => m.type === MediaType.VIDEO);
|
|
1421
|
|
-
|
|
1422
|
|
- if (!mLines.length || !browser.supportsDDExtHeaders()) {
|
|
1423
|
|
- return description;
|
|
1424
|
|
- }
|
|
1425
|
|
-
|
|
1426
|
|
- mLines.forEach((mLine, idx) => {
|
|
1427
|
|
- const senderMids = Array.from(this._localTrackTransceiverMids.values());
|
|
1428
|
|
- const isSender = senderMids.length
|
|
1429
|
|
- ? senderMids.find(mid => mLine.mid.toString() === mid.toString())
|
|
1430
|
|
- : idx === 0;
|
|
1431
|
|
- const payload = mLine.payloads.split(' ')[0];
|
|
1432
|
|
- let { codec } = mLine.rtp.find(rtp => rtp.payload === Number(payload));
|
|
1433
|
|
-
|
|
1434
|
|
- codec = codec.toLowerCase();
|
|
1435
|
|
-
|
|
1436
|
|
- if (isSender && mLine.ext?.length) {
|
|
1437
|
|
- const headerIndex = mLine.ext.findIndex(ext => ext.uri === DD_HEADER_EXT_URI);
|
|
1438
|
|
- const shouldNegotiateHeaderExts = codec === CodecMimeType.AV1 || codec === CodecMimeType.H264;
|
|
1439
|
|
-
|
|
1440
|
|
- if (!this._supportsDDHeaderExt && headerIndex >= 0) {
|
|
1441
|
|
- this._supportsDDHeaderExt = true;
|
|
1442
|
|
- }
|
|
1443
|
|
-
|
|
1444
|
|
- if (this._supportsDDHeaderExt && shouldNegotiateHeaderExts && headerIndex < 0) {
|
|
1445
|
|
- mLine.ext.push({
|
|
1446
|
|
- value: DD_HEADER_EXT_ID,
|
|
1447
|
|
- uri: DD_HEADER_EXT_URI
|
|
1448
|
|
- });
|
|
1449
|
|
- } else if (!shouldNegotiateHeaderExts && headerIndex >= 0) {
|
|
1450
|
|
- mLine.ext.splice(headerIndex, 1);
|
|
1451
|
|
- }
|
|
1452
|
|
- }
|
|
1453
|
|
- });
|
|
1454
|
|
-
|
|
1455
|
|
- return new RTCSessionDescription({
|
|
1456
|
|
- type: description.type,
|
|
1457
|
|
- sdp: transform.write(parsedSdp)
|
|
1458
|
|
- });
|
|
1459
|
|
-};
|
|
1460
|
|
-
|
|
1461
|
1328
|
/**
|
|
1462
|
1329
|
* Add {@link JitsiLocalTrack} to this TPC.
|
|
1463
|
1330
|
* @param {JitsiLocalTrack} track
|
|
|
@@ -1466,30 +1333,54 @@ TraceablePeerConnection.prototype._updateAv1DdHeaders = function(description) {
|
|
1466
|
1333
|
*/
|
|
1467
|
1334
|
TraceablePeerConnection.prototype.addTrack = function(track, isInitiator = false) {
|
|
1468
|
1335
|
const rtcId = track.rtcId;
|
|
1469
|
|
- let transceiver;
|
|
1470
|
1336
|
|
|
1471
|
|
- logger.info(`${this} adding ${track}`);
|
|
1472
|
1337
|
if (this.localTracks.has(rtcId)) {
|
|
1473
|
|
-
|
|
1474
|
1338
|
return Promise.reject(new Error(`${track} is already in ${this}`));
|
|
1475
|
1339
|
}
|
|
1476
|
1340
|
|
|
1477
|
|
- this.localTracks.set(rtcId, track);
|
|
|
1341
|
+ logger.info(`${this} adding ${track}`);
|
|
1478
|
1342
|
const webrtcStream = track.getOriginalStream();
|
|
|
1343
|
+ const mediaStreamTrack = track.getTrack();
|
|
|
1344
|
+ let transceiver;
|
|
|
1345
|
+
|
|
|
1346
|
+ if (isInitiator) {
|
|
|
1347
|
+ const streams = [];
|
|
1479
|
1348
|
|
|
1480
|
|
- try {
|
|
1481
|
|
- transceiver = this.tpcUtils.addTrack(track, isInitiator);
|
|
1482
|
|
- } catch (error) {
|
|
1483
|
|
- logger.error(`${this} Adding track=${track} failed: ${error?.message}`);
|
|
|
1349
|
+ webrtcStream && streams.push(webrtcStream);
|
|
1484
|
1350
|
|
|
1485
|
|
- return Promise.reject(error);
|
|
|
1351
|
+ // Use pc.addTransceiver() for the initiator case when local tracks are getting added
|
|
|
1352
|
+ // to the peerconnection before a session-initiate is sent over to the peer.
|
|
|
1353
|
+ const transceiverInit = {
|
|
|
1354
|
+ direction: MediaDirection.SENDRECV,
|
|
|
1355
|
+ streams,
|
|
|
1356
|
+ sendEncodings: []
|
|
|
1357
|
+ };
|
|
|
1358
|
+
|
|
|
1359
|
+ if (!browser.isFirefox()) {
|
|
|
1360
|
+ transceiverInit.sendEncodings = this.tpcUtils.getStreamEncodings(track);
|
|
|
1361
|
+ }
|
|
|
1362
|
+
|
|
|
1363
|
+ try {
|
|
|
1364
|
+ transceiver = this.peerconnection.addTransceiver(mediaStreamTrack, transceiverInit);
|
|
|
1365
|
+ } catch (error) {
|
|
|
1366
|
+ return Promise.reject(new Error(`${this} failed to create transceiver for ${track} - ${error}`));
|
|
|
1367
|
+ }
|
|
|
1368
|
+ } else {
|
|
|
1369
|
+ // Use pc.addTrack() for responder case so that we can re-use the m-lines that were created
|
|
|
1370
|
+ // when setRemoteDescription was called. pc.addTrack() automatically attaches to any existing
|
|
|
1371
|
+ // unused "recv-only" transceiver.
|
|
|
1372
|
+ const sender = this.peerconnection.addTrack(mediaStreamTrack);
|
|
|
1373
|
+
|
|
|
1374
|
+ // Find the corresponding transceiver that the track was attached to.
|
|
|
1375
|
+ transceiver = this.peerconnection.getTransceivers().find(t => t.sender === sender);
|
|
1486
|
1376
|
}
|
|
1487
|
1377
|
|
|
1488
|
1378
|
if (transceiver?.mid) {
|
|
1489
|
|
- this._localTrackTransceiverMids.set(track.rtcId, transceiver.mid.toString());
|
|
|
1379
|
+ this.localTrackTransceiverMids.set(track.rtcId, transceiver.mid.toString());
|
|
1490
|
1380
|
}
|
|
1491
|
1381
|
|
|
1492
|
1382
|
if (track) {
|
|
|
1383
|
+ this.localTracks.set(rtcId, track);
|
|
1493
|
1384
|
if (track.isAudioTrack()) {
|
|
1494
|
1385
|
this._hasHadAudioTrack = true;
|
|
1495
|
1386
|
} else {
|
|
|
@@ -1501,7 +1392,7 @@ TraceablePeerConnection.prototype.addTrack = function(track, isInitiator = false
|
|
1501
|
1392
|
|
|
1502
|
1393
|
// On Firefox, the encodings have to be configured on the sender only after the transceiver is created.
|
|
1503
|
1394
|
if (browser.isFirefox()) {
|
|
1504
|
|
- promiseChain = promiseChain.then(() => webrtcStream && this.tpcUtils.setEncodings(track));
|
|
|
1395
|
+ promiseChain = promiseChain.then(() => webrtcStream && this._setEncodings(track));
|
|
1505
|
1396
|
}
|
|
1506
|
1397
|
|
|
1507
|
1398
|
return promiseChain;
|
|
|
@@ -1531,7 +1422,7 @@ TraceablePeerConnection.prototype.addTrackToPc = function(track) {
|
|
1531
|
1422
|
return Promise.reject('Stream not found');
|
|
1532
|
1423
|
}
|
|
1533
|
1424
|
|
|
1534
|
|
- return this.tpcUtils.replaceTrack(null, track).then(() => {
|
|
|
1425
|
+ return this.replaceTrack(null, track, true /* isMuteOperation */).then(() => {
|
|
1535
|
1426
|
if (track) {
|
|
1536
|
1427
|
if (track.isAudioTrack()) {
|
|
1537
|
1428
|
this._hasHadAudioTrack = true;
|
|
|
@@ -1567,42 +1458,6 @@ TraceablePeerConnection.prototype._assertTrackBelongs = function(
|
|
1567
|
1458
|
return doesBelong;
|
|
1568
|
1459
|
};
|
|
1569
|
1460
|
|
|
1570
|
|
-/**
|
|
1571
|
|
- * Returns the codec that is configured on the client as the preferred video codec for the given local video track.
|
|
1572
|
|
- *
|
|
1573
|
|
- * @param {JitsiLocalTrack} localTrack - The local video track.
|
|
1574
|
|
- * @returns {CodecMimeType} The codec that is set as the preferred codec for the given local video track.
|
|
1575
|
|
- *
|
|
1576
|
|
- */
|
|
1577
|
|
-TraceablePeerConnection.prototype.getConfiguredVideoCodec = function(localTrack) {
|
|
1578
|
|
- const localVideoTrack = localTrack ?? this.getLocalVideoTracks()[0];
|
|
1579
|
|
- const rtpSender = this.findSenderForTrack(localVideoTrack.getTrack());
|
|
1580
|
|
-
|
|
1581
|
|
- if (this.usesCodecSelectionAPI() && rtpSender) {
|
|
1582
|
|
- const { codecs } = rtpSender.getParameters();
|
|
1583
|
|
-
|
|
1584
|
|
- return codecs[0].mimeType.split('/')[1].toLowerCase();
|
|
1585
|
|
- }
|
|
1586
|
|
-
|
|
1587
|
|
- const sdp = this.remoteDescription?.sdp;
|
|
1588
|
|
- const defaultCodec = CodecMimeType.VP8;
|
|
1589
|
|
-
|
|
1590
|
|
- if (!sdp) {
|
|
1591
|
|
- return defaultCodec;
|
|
1592
|
|
- }
|
|
1593
|
|
- const parsedSdp = transform.parse(sdp);
|
|
1594
|
|
- const mLine = parsedSdp.media
|
|
1595
|
|
- .find(m => m.mid.toString() === this._localTrackTransceiverMids.get(localVideoTrack.rtcId));
|
|
1596
|
|
- const payload = mLine.payloads.split(' ')[0];
|
|
1597
|
|
- const { codec } = mLine.rtp.find(rtp => rtp.payload === Number(payload));
|
|
1598
|
|
-
|
|
1599
|
|
- if (codec) {
|
|
1600
|
|
- return Object.values(CodecMimeType).find(value => value === codec.toLowerCase());
|
|
1601
|
|
- }
|
|
1602
|
|
-
|
|
1603
|
|
- return defaultCodec;
|
|
1604
|
|
-};
|
|
1605
|
|
-
|
|
1606
|
1461
|
/**
|
|
1607
|
1462
|
* Returns the codecs in the current order of preference as configured on the peerconnection.
|
|
1608
|
1463
|
*
|
|
|
@@ -1610,18 +1465,7 @@ TraceablePeerConnection.prototype.getConfiguredVideoCodec = function(localTrack)
|
|
1610
|
1465
|
* @returns {Array}
|
|
1611
|
1466
|
*/
|
|
1612
|
1467
|
TraceablePeerConnection.prototype.getConfiguredVideoCodecs = function(description) {
|
|
1613
|
|
- const currentSdp = description?.sdp ?? this.localDescription?.sdp;
|
|
1614
|
|
-
|
|
1615
|
|
- if (!currentSdp) {
|
|
1616
|
|
- return [];
|
|
1617
|
|
- }
|
|
1618
|
|
- const parsedSdp = transform.parse(currentSdp);
|
|
1619
|
|
- const mLine = parsedSdp.media.find(m => m.type === MediaType.VIDEO);
|
|
1620
|
|
- const codecs = new Set(mLine.rtp
|
|
1621
|
|
- .filter(pt => pt.codec.toLowerCase() !== 'rtx')
|
|
1622
|
|
- .map(pt => pt.codec.toLowerCase()));
|
|
1623
|
|
-
|
|
1624
|
|
- return Array.from(codecs);
|
|
|
1468
|
+ return this.tpcUtils.getConfiguredVideoCodecs(description);
|
|
1625
|
1469
|
};
|
|
1626
|
1470
|
|
|
1627
|
1471
|
/**
|
|
|
@@ -1725,8 +1569,8 @@ TraceablePeerConnection.prototype.processLocalSdpForTransceiverInfo = function(l
|
|
1725
|
1569
|
const mLines = parsedSdp.media.filter(mline => mline.type === mediaType);
|
|
1726
|
1570
|
|
|
1727
|
1571
|
tracks.forEach((track, idx) => {
|
|
1728
|
|
- if (!this._localTrackTransceiverMids.has(track.rtcId)) {
|
|
1729
|
|
- this._localTrackTransceiverMids.set(track.rtcId, mLines[idx].mid.toString());
|
|
|
1572
|
+ if (!this.localTrackTransceiverMids.has(track.rtcId)) {
|
|
|
1573
|
+ this.localTrackTransceiverMids.set(track.rtcId, mLines[idx].mid.toString());
|
|
1730
|
1574
|
}
|
|
1731
|
1575
|
});
|
|
1732
|
1576
|
});
|
|
|
@@ -1740,11 +1584,11 @@ TraceablePeerConnection.prototype.processLocalSdpForTransceiverInfo = function(l
|
|
1740
|
1584
|
*
|
|
1741
|
1585
|
* @param {JitsiLocalTrack|null} oldTrack - The current track in use to be replaced on the pc.
|
|
1742
|
1586
|
* @param {JitsiLocalTrack|null} newTrack - The new track to be used.
|
|
1743
|
|
- *
|
|
|
1587
|
+ * @param {boolean} isMuteOperation - Whether the operation is a mute/unmute operation.
|
|
1744
|
1588
|
* @returns {Promise<boolean>} - If the promise resolves with true, renegotiation will be needed.
|
|
1745
|
1589
|
* Otherwise no renegotiation is needed.
|
|
1746
|
1590
|
*/
|
|
1747
|
|
-TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {
|
|
|
1591
|
+TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack, isMuteOperation = false) {
|
|
1748
|
1592
|
if (!(oldTrack || newTrack)) {
|
|
1749
|
1593
|
logger.info(`${this} replaceTrack called with no new track and no old track`);
|
|
1750
|
1594
|
|
|
|
@@ -1753,11 +1597,68 @@ TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {
|
|
1753
|
1597
|
|
|
1754
|
1598
|
logger.info(`${this} TPC.replaceTrack old=${oldTrack}, new=${newTrack}`);
|
|
1755
|
1599
|
|
|
1756
|
|
- return this.tpcUtils.replaceTrack(oldTrack, newTrack)
|
|
1757
|
|
- .then(transceiver => {
|
|
|
1600
|
+ let transceiver;
|
|
|
1601
|
+ const mediaType = newTrack?.getType() ?? oldTrack?.getType();
|
|
|
1602
|
+ const localTracks = this.getLocalTracks(mediaType);
|
|
|
1603
|
+ const track = newTrack?.getTrack() ?? null;
|
|
|
1604
|
+ const isNewLocalSource = localTracks?.length
|
|
|
1605
|
+ && !oldTrack
|
|
|
1606
|
+ && newTrack
|
|
|
1607
|
+ && !localTracks.find(t => t === newTrack);
|
|
|
1608
|
+
|
|
|
1609
|
+ // If old track exists, replace the track on the corresponding sender.
|
|
|
1610
|
+ if (oldTrack && !oldTrack.isMuted()) {
|
|
|
1611
|
+ transceiver = this.peerconnection.getTransceivers().find(t => t.sender.track === oldTrack.getTrack());
|
|
|
1612
|
+
|
|
|
1613
|
+ // Find the first recvonly transceiver when more than one track of the same media type is being added to the pc.
|
|
|
1614
|
+ // As part of the track addition, a new m-line was added to the remote description with direction set to
|
|
|
1615
|
+ // recvonly.
|
|
|
1616
|
+ } else if (isNewLocalSource) {
|
|
|
1617
|
+ transceiver = this.peerconnection.getTransceivers().find(
|
|
|
1618
|
+ t => t.receiver.track.kind === mediaType
|
|
|
1619
|
+ && t.direction === MediaDirection.RECVONLY
|
|
|
1620
|
+
|
|
|
1621
|
+ // Re-use any existing recvonly transceiver (if available) for p2p case.
|
|
|
1622
|
+ && ((this.isP2P && t.currentDirection === MediaDirection.RECVONLY)
|
|
|
1623
|
+ || (t.currentDirection === MediaDirection.INACTIVE && !t.stopped)));
|
|
|
1624
|
+
|
|
|
1625
|
+ // For mute/unmute operations, find the transceiver based on the track index in the source name if present,
|
|
|
1626
|
+ // otherwise it is assumed to be the first local track that was added to the peerconnection.
|
|
|
1627
|
+ } else {
|
|
|
1628
|
+ transceiver = this.peerconnection.getTransceivers().find(t => t.receiver.track.kind === mediaType);
|
|
|
1629
|
+ const sourceName = newTrack?.getSourceName() ?? oldTrack?.getSourceName();
|
|
|
1630
|
+
|
|
|
1631
|
+ if (sourceName) {
|
|
|
1632
|
+ const trackIndex = getSourceIndexFromSourceName(sourceName);
|
|
|
1633
|
+
|
|
|
1634
|
+ if (this.isP2P) {
|
|
|
1635
|
+ transceiver = this.peerconnection.getTransceivers()
|
|
|
1636
|
+ .filter(t => t.receiver.track.kind === mediaType)[trackIndex];
|
|
|
1637
|
+ } else if (oldTrack) {
|
|
|
1638
|
+ const transceiverMid = this.localTrackTransceiverMids.get(oldTrack.rtcId);
|
|
|
1639
|
+
|
|
|
1640
|
+ transceiver = this.peerconnection.getTransceivers().find(t => t.mid === transceiverMid);
|
|
|
1641
|
+ } else if (trackIndex) {
|
|
|
1642
|
+ transceiver = this.peerconnection.getTransceivers()
|
|
|
1643
|
+ .filter(t => t.receiver.track.kind === mediaType
|
|
|
1644
|
+ && t.direction !== MediaDirection.RECVONLY)[trackIndex];
|
|
|
1645
|
+ }
|
|
|
1646
|
+ }
|
|
|
1647
|
+ }
|
|
|
1648
|
+
|
|
|
1649
|
+ if (!transceiver) {
|
|
|
1650
|
+ return Promise.reject(
|
|
|
1651
|
+ new Error(`Replace track failed - no transceiver for old: ${oldTrack}, new: ${newTrack}`));
|
|
|
1652
|
+ }
|
|
|
1653
|
+
|
|
|
1654
|
+ return transceiver.sender.replaceTrack(track)
|
|
|
1655
|
+ .then(() => {
|
|
|
1656
|
+ if (isMuteOperation) {
|
|
|
1657
|
+ return Promise.resolve();
|
|
|
1658
|
+ }
|
|
1758
|
1659
|
if (oldTrack) {
|
|
1759
|
1660
|
this.localTracks.delete(oldTrack.rtcId);
|
|
1760
|
|
- this._localTrackTransceiverMids.delete(oldTrack.rtcId);
|
|
|
1661
|
+ this.localTrackTransceiverMids.delete(oldTrack.rtcId);
|
|
1761
|
1662
|
}
|
|
1762
|
1663
|
|
|
1763
|
1664
|
if (newTrack) {
|
|
|
@@ -1766,7 +1667,7 @@ TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {
|
|
1766
|
1667
|
} else {
|
|
1767
|
1668
|
this._hasHadVideoTrack = true;
|
|
1768
|
1669
|
}
|
|
1769
|
|
- this._localTrackTransceiverMids.set(newTrack.rtcId, transceiver?.mid?.toString());
|
|
|
1670
|
+ this.localTrackTransceiverMids.set(newTrack.rtcId, transceiver?.mid?.toString());
|
|
1770
|
1671
|
this.localTracks.set(newTrack.rtcId, newTrack);
|
|
1771
|
1672
|
}
|
|
1772
|
1673
|
|
|
|
@@ -1804,7 +1705,7 @@ TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {
|
|
1804
|
1705
|
const configureEncodingsPromise
|
|
1805
|
1706
|
= !newTrack || browser.usesSdpMungingForSimulcast()
|
|
1806
|
1707
|
? Promise.resolve()
|
|
1807
|
|
- : this.tpcUtils.setEncodings(newTrack);
|
|
|
1708
|
+ : this._setEncodings(newTrack);
|
|
1808
|
1709
|
|
|
1809
|
1710
|
return configureEncodingsPromise.then(() => this.isP2P);
|
|
1810
|
1711
|
});
|
|
|
@@ -1827,7 +1728,7 @@ TraceablePeerConnection.prototype.removeTrackFromPc = function(localTrack) {
|
|
1827
|
1728
|
return Promise.reject('Track not found in the peerconnection');
|
|
1828
|
1729
|
}
|
|
1829
|
1730
|
|
|
1830
|
|
- return this.tpcUtils.replaceTrack(localTrack, null).then(() => false);
|
|
|
1731
|
+ return this.replaceTrack(localTrack, null, true /* isMuteOperation */).then(() => false);
|
|
1831
|
1732
|
};
|
|
1832
|
1733
|
|
|
1833
|
1734
|
/**
|
|
|
@@ -1937,81 +1838,6 @@ TraceablePeerConnection.prototype._getPreferredCodecForScreenshare = function(de
|
|
1937
|
1838
|
return defaultCodec;
|
|
1938
|
1839
|
};
|
|
1939
|
1840
|
|
|
1940
|
|
-/**
|
|
1941
|
|
- * Munges the stereo flag as well as the opusMaxAverageBitrate in the SDP, based
|
|
1942
|
|
- * on values set through config.js, if present.
|
|
1943
|
|
- *
|
|
1944
|
|
- * @param {RTCSessionDescription} description that needs to be munged.
|
|
1945
|
|
- * @returns {RTCSessionDescription} the munged description.
|
|
1946
|
|
- */
|
|
1947
|
|
-TraceablePeerConnection.prototype._mungeOpus = function(description) {
|
|
1948
|
|
- const { audioQuality } = this.options;
|
|
1949
|
|
-
|
|
1950
|
|
- if (!audioQuality?.enableOpusDtx && !audioQuality?.stereo && !audioQuality?.opusMaxAverageBitrate) {
|
|
1951
|
|
- return description;
|
|
1952
|
|
- }
|
|
1953
|
|
-
|
|
1954
|
|
- const parsedSdp = transform.parse(description.sdp);
|
|
1955
|
|
- const mLines = parsedSdp.media;
|
|
1956
|
|
-
|
|
1957
|
|
- for (const mLine of mLines) {
|
|
1958
|
|
- if (mLine.type === 'audio') {
|
|
1959
|
|
- const { payload } = mLine.rtp.find(protocol => protocol.codec === CodecMimeType.OPUS);
|
|
1960
|
|
-
|
|
1961
|
|
- if (!payload) {
|
|
1962
|
|
- // eslint-disable-next-line no-continue
|
|
1963
|
|
- continue;
|
|
1964
|
|
- }
|
|
1965
|
|
-
|
|
1966
|
|
- let fmtpOpus = mLine.fmtp.find(protocol => protocol.payload === payload);
|
|
1967
|
|
-
|
|
1968
|
|
- if (!fmtpOpus) {
|
|
1969
|
|
- fmtpOpus = {
|
|
1970
|
|
- payload,
|
|
1971
|
|
- config: ''
|
|
1972
|
|
- };
|
|
1973
|
|
- }
|
|
1974
|
|
-
|
|
1975
|
|
- const fmtpConfig = transform.parseParams(fmtpOpus.config);
|
|
1976
|
|
- let sdpChanged = false;
|
|
1977
|
|
-
|
|
1978
|
|
- if (audioQuality?.stereo) {
|
|
1979
|
|
- fmtpConfig.stereo = 1;
|
|
1980
|
|
- sdpChanged = true;
|
|
1981
|
|
- }
|
|
1982
|
|
-
|
|
1983
|
|
- if (audioQuality?.opusMaxAverageBitrate) {
|
|
1984
|
|
- fmtpConfig.maxaveragebitrate = audioQuality.opusMaxAverageBitrate;
|
|
1985
|
|
- sdpChanged = true;
|
|
1986
|
|
- }
|
|
1987
|
|
-
|
|
1988
|
|
- // On Firefox, the OpusDtx enablement has no effect
|
|
1989
|
|
- if (!browser.isFirefox() && audioQuality?.enableOpusDtx) {
|
|
1990
|
|
- fmtpConfig.usedtx = 1;
|
|
1991
|
|
- sdpChanged = true;
|
|
1992
|
|
- }
|
|
1993
|
|
-
|
|
1994
|
|
- if (!sdpChanged) {
|
|
1995
|
|
- // eslint-disable-next-line no-continue
|
|
1996
|
|
- continue;
|
|
1997
|
|
- }
|
|
1998
|
|
-
|
|
1999
|
|
- let mungedConfig = '';
|
|
2000
|
|
-
|
|
2001
|
|
- for (const key of Object.keys(fmtpConfig)) {
|
|
2002
|
|
- mungedConfig += `${key}=${fmtpConfig[key]}; `;
|
|
2003
|
|
- }
|
|
2004
|
|
-
|
|
2005
|
|
- fmtpOpus.config = mungedConfig.trim();
|
|
2006
|
|
- }
|
|
2007
|
|
- }
|
|
2008
|
|
-
|
|
2009
|
|
- return new RTCSessionDescription({
|
|
2010
|
|
- type: description.type,
|
|
2011
|
|
- sdp: transform.write(parsedSdp)
|
|
2012
|
|
- });
|
|
2013
|
|
-};
|
|
2014
|
|
-
|
|
2015
|
1841
|
/**
|
|
2016
|
1842
|
* Sets up the _dtlsTransport object and initializes callbacks for it.
|
|
2017
|
1843
|
*/
|
|
|
@@ -2036,70 +1862,6 @@ TraceablePeerConnection.prototype._initializeDtlsTransport = function() {
|
|
2036
|
1862
|
}
|
|
2037
|
1863
|
};
|
|
2038
|
1864
|
|
|
2039
|
|
-/**
|
|
2040
|
|
- * Sets the max bitrates on the video m-lines when VP9/AV1 is the selected codec.
|
|
2041
|
|
- *
|
|
2042
|
|
- * @param {RTCSessionDescription} description - The local description that needs to be munged.
|
|
2043
|
|
- * @param {boolean} isLocalSdp - Whether the max bitrate (via b=AS line in SDP) is set on local SDP.
|
|
2044
|
|
- * @returns RTCSessionDescription
|
|
2045
|
|
- */
|
|
2046
|
|
-TraceablePeerConnection.prototype._setMaxBitrates = function(description, isLocalSdp = false) {
|
|
2047
|
|
- if (!this.codecSettings) {
|
|
2048
|
|
- return description;
|
|
2049
|
|
- }
|
|
2050
|
|
- const parsedSdp = transform.parse(description.sdp);
|
|
2051
|
|
-
|
|
2052
|
|
- // Find all the m-lines associated with the local sources.
|
|
2053
|
|
- const direction = isLocalSdp ? MediaDirection.RECVONLY : MediaDirection.SENDONLY;
|
|
2054
|
|
- const mLines = parsedSdp.media.filter(m => m.type === MediaType.VIDEO && m.direction !== direction);
|
|
2055
|
|
- const currentCodec = this.codecSettings.codecList[0];
|
|
2056
|
|
- const codecScalabilityModeSettings = this.tpcUtils.codecSettings[currentCodec];
|
|
2057
|
|
-
|
|
2058
|
|
- for (const mLine of mLines) {
|
|
2059
|
|
- const isDoingVp9KSvc = currentCodec === CodecMimeType.VP9
|
|
2060
|
|
- && !codecScalabilityModeSettings.scalabilityModeEnabled;
|
|
2061
|
|
- const localTrack = this.getLocalVideoTracks()
|
|
2062
|
|
- .find(track => this._localTrackTransceiverMids.get(track.rtcId) === mLine.mid.toString());
|
|
2063
|
|
-
|
|
2064
|
|
- if (localTrack
|
|
2065
|
|
- && (isDoingVp9KSvc
|
|
2066
|
|
-
|
|
2067
|
|
- // Setting bitrates in the SDP for SVC codecs is no longer needed in the newer versions where
|
|
2068
|
|
- // maxBitrates from the RTCRtpEncodingParameters directly affect the target bitrate for the encoder.
|
|
2069
|
|
- || (this.tpcUtils._isRunningInFullSvcMode(currentCodec) && !this.usesCodecSelectionAPI()))) {
|
|
2070
|
|
- let maxBitrate;
|
|
2071
|
|
-
|
|
2072
|
|
- if (localTrack.getVideoType() === VideoType.DESKTOP) {
|
|
2073
|
|
- maxBitrate = codecScalabilityModeSettings.maxBitratesVideo.ssHigh;
|
|
2074
|
|
- } else {
|
|
2075
|
|
- const { level } = VIDEO_QUALITY_LEVELS.find(lvl => lvl.height <= localTrack.getCaptureResolution());
|
|
2076
|
|
-
|
|
2077
|
|
- maxBitrate = codecScalabilityModeSettings.maxBitratesVideo[level];
|
|
2078
|
|
- }
|
|
2079
|
|
-
|
|
2080
|
|
- const limit = Math.floor(maxBitrate / 1000);
|
|
2081
|
|
-
|
|
2082
|
|
- // Use only the highest spatial layer bitrates for now as there is no API available yet for configuring
|
|
2083
|
|
- // the bitrates on the individual SVC layers.
|
|
2084
|
|
- mLine.bandwidth = [ {
|
|
2085
|
|
- type: 'AS',
|
|
2086
|
|
- limit
|
|
2087
|
|
- } ];
|
|
2088
|
|
- } else {
|
|
2089
|
|
- // Clear the bandwidth limit in SDP when VP9 is no longer the preferred codec.
|
|
2090
|
|
- // This is needed on react native clients as react-native-webrtc returns the
|
|
2091
|
|
- // SDP that the application passed instead of returning the SDP off the native side.
|
|
2092
|
|
- // This line automatically gets cleared on web on every renegotiation.
|
|
2093
|
|
- mLine.bandwidth = undefined;
|
|
2094
|
|
- }
|
|
2095
|
|
- }
|
|
2096
|
|
-
|
|
2097
|
|
- return new RTCSessionDescription({
|
|
2098
|
|
- type: description.type,
|
|
2099
|
|
- sdp: transform.write(parsedSdp)
|
|
2100
|
|
- });
|
|
2101
|
|
-};
|
|
2102
|
|
-
|
|
2103
|
1865
|
/**
|
|
2104
|
1866
|
* Returns the expected send resolution for a local video track based on what encodings are currently active.
|
|
2105
|
1867
|
*
|
|
|
@@ -2144,17 +1906,61 @@ TraceablePeerConnection.prototype.calculateExpectedSendResolution = function(loc
|
|
2144
|
1906
|
*/
|
|
2145
|
1907
|
TraceablePeerConnection.prototype.configureAudioSenderEncodings = function(localAudioTrack = null) {
|
|
2146
|
1908
|
if (localAudioTrack) {
|
|
2147
|
|
- return this.tpcUtils.setEncodings(localAudioTrack);
|
|
|
1909
|
+ return this._setEncodings(localAudioTrack);
|
|
2148
|
1910
|
}
|
|
2149
|
1911
|
const promises = [];
|
|
2150
|
1912
|
|
|
2151
|
1913
|
for (const track of this.getLocalTracks(MediaType.AUDIO)) {
|
|
2152
|
|
- promises.push(this.tpcUtils.setEncodings(track));
|
|
|
1914
|
+ promises.push(this._setEncodings(track));
|
|
2153
|
1915
|
}
|
|
2154
|
1916
|
|
|
2155
|
1917
|
return Promise.allSettled(promises);
|
|
2156
|
1918
|
};
|
|
2157
|
1919
|
|
|
|
1920
|
+/**
|
|
|
1921
|
+ * Configures the RTCRtpEncodingParameters of the outbound rtp stream associated with the given track.
|
|
|
1922
|
+ *
|
|
|
1923
|
+ * @param {JitsiLocalTracj} localTrack - The local track whose outbound stream needs to be configured.
|
|
|
1924
|
+ * @returns {Promise} - A promise that resolves when the operation is successful, rejected otherwise.
|
|
|
1925
|
+ */
|
|
|
1926
|
+TraceablePeerConnection.prototype._configureSenderEncodings = function(localTrack) {
|
|
|
1927
|
+ const mediaType = localTrack.getType();
|
|
|
1928
|
+ const transceiver = localTrack?.track && localTrack.getOriginalStream()
|
|
|
1929
|
+ ? this.peerconnection.getTransceivers().find(t => t.sender?.track?.id === localTrack.getTrackId())
|
|
|
1930
|
+ : this.peerconnection.getTransceivers().find(t => t.receiver?.track?.kind === mediaType);
|
|
|
1931
|
+ const parameters = transceiver?.sender?.getParameters();
|
|
|
1932
|
+
|
|
|
1933
|
+ // Resolve if the encodings are not available yet. This happens immediately after the track is added to the
|
|
|
1934
|
+ // peerconnection on chrome in unified-plan. It is ok to ignore and not report the error here since the
|
|
|
1935
|
+ // action that triggers 'addTrack' (like unmute) will also configure the encodings and set bitrates after that.
|
|
|
1936
|
+ if (!parameters?.encodings?.length) {
|
|
|
1937
|
+ return Promise.resolve();
|
|
|
1938
|
+ }
|
|
|
1939
|
+
|
|
|
1940
|
+ parameters.encodings = this.tpcUtils.getStreamEncodings(localTrack);
|
|
|
1941
|
+
|
|
|
1942
|
+ return transceiver.sender.setParameters(parameters);
|
|
|
1943
|
+};
|
|
|
1944
|
+
|
|
|
1945
|
+/**
|
|
|
1946
|
+ * Enables/disables the streams by changing the active field on RTCRtpEncodingParameters for a given RTCRtpSender.
|
|
|
1947
|
+ *
|
|
|
1948
|
+ * @param {RTCRtpSender} sender - the sender associated with a MediaStreamTrack.
|
|
|
1949
|
+ * @param {boolean} enable - whether the streams needs to be enabled or disabled.
|
|
|
1950
|
+ * @returns {Promise} - A promise that resolves when the operation is successful, rejected otherwise.
|
|
|
1951
|
+ */
|
|
|
1952
|
+TraceablePeerConnection.prototype._enableSenderEncodings = function(sender, enable) {
|
|
|
1953
|
+ const parameters = sender.getParameters();
|
|
|
1954
|
+
|
|
|
1955
|
+ if (parameters?.encodings?.length) {
|
|
|
1956
|
+ for (const encoding of parameters.encodings) {
|
|
|
1957
|
+ encoding.active = enable;
|
|
|
1958
|
+ }
|
|
|
1959
|
+ }
|
|
|
1960
|
+
|
|
|
1961
|
+ return sender.setParameters(parameters);
|
|
|
1962
|
+};
|
|
|
1963
|
+
|
|
2158
|
1964
|
/**
|
|
2159
|
1965
|
* Configures the stream encodings depending on the video type, scalability mode and the bitrate settings for the codec
|
|
2160
|
1966
|
* that is currently selected.
|
|
|
@@ -2182,20 +1988,50 @@ TraceablePeerConnection.prototype.configureVideoSenderEncodings = function(local
|
|
2182
|
1988
|
return Promise.allSettled(promises);
|
|
2183
|
1989
|
};
|
|
2184
|
1990
|
|
|
2185
|
|
-TraceablePeerConnection.prototype.setLocalDescription = function(description) {
|
|
2186
|
|
- let localDescription = description;
|
|
|
1991
|
+/**
|
|
|
1992
|
+ * Set the simulcast stream encoding properties on the RTCRtpSender.
|
|
|
1993
|
+ *
|
|
|
1994
|
+ * @param {JitsiLocalTrack} localTrack - the current track in use for which the encodings are to be set.
|
|
|
1995
|
+ * @returns {Promise<void>} - resolved when done.
|
|
|
1996
|
+ */
|
|
|
1997
|
+TraceablePeerConnection.prototype._setEncodings = function(localTrack) {
|
|
|
1998
|
+ if (localTrack.getType() === MediaType.VIDEO) {
|
|
|
1999
|
+ return this._updateVideoSenderParameters(() => this._configureSenderEncodings(localTrack));
|
|
|
2000
|
+ }
|
|
2187
|
2001
|
|
|
2188
|
|
- this.trace('setLocalDescription::preTransform', dumpSDP(localDescription));
|
|
|
2002
|
+ return this._configureSenderEncodings(localTrack);
|
|
|
2003
|
+};
|
|
2189
|
2004
|
|
|
2190
|
|
- // Munge stereo flag and opusMaxAverageBitrate based on config.js
|
|
2191
|
|
- localDescription = this._mungeOpus(localDescription);
|
|
|
2005
|
+/**
|
|
|
2006
|
+ * Munges the provided description to update the codec order, set the max bitrates (for VP9 K-SVC), set stereo flag
|
|
|
2007
|
+ * and update the DD Header extensions for AV1.
|
|
|
2008
|
+ *
|
|
|
2009
|
+ * @param {RTCSessionDescription} description - The description to be munged.
|
|
|
2010
|
+ * @returns {RTCSessionDescription} - The munged description.
|
|
|
2011
|
+ */
|
|
|
2012
|
+TraceablePeerConnection.prototype._mungeDescription = function(description) {
|
|
|
2013
|
+ let mungedDescription = description;
|
|
|
2014
|
+
|
|
|
2015
|
+ this.trace('RTCSessionDescription::preTransform', dumpSDP(description));
|
|
|
2016
|
+ mungedDescription = this.tpcUtils.mungeOpus(description);
|
|
|
2017
|
+ mungedDescription = this.tpcUtils.mungeCodecOrder(mungedDescription);
|
|
|
2018
|
+ mungedDescription = this.tpcUtils.setMaxBitrates(mungedDescription, true);
|
|
|
2019
|
+ mungedDescription = this.tpcUtils.updateAv1DdHeaders(mungedDescription);
|
|
|
2020
|
+ this.trace('RTCSessionDescription::postTransform', dumpSDP(mungedDescription));
|
|
|
2021
|
+
|
|
|
2022
|
+ return mungedDescription;
|
|
|
2023
|
+};
|
|
2192
|
2024
|
|
|
2193
|
|
- // Munge the order of the codecs based on the preferences set through config.js.
|
|
2194
|
|
- localDescription = this._mungeCodecOrder(localDescription);
|
|
2195
|
|
- localDescription = this._setMaxBitrates(localDescription, true);
|
|
2196
|
|
- localDescription = this._updateAv1DdHeaders(localDescription);
|
|
|
2025
|
+/**
|
|
|
2026
|
+ * Sets the local description on the peerconnection.
|
|
|
2027
|
+ *
|
|
|
2028
|
+ * @param {RTCSessionDescription} description - The local description to be set.
|
|
|
2029
|
+ * @returns {Promise<void>} - Resolved when the operation is successful and rejected with an error otherwise.
|
|
|
2030
|
+ */
|
|
|
2031
|
+TraceablePeerConnection.prototype.setLocalDescription = function(description) {
|
|
|
2032
|
+ let localDescription = description;
|
|
2197
|
2033
|
|
|
2198
|
|
- this.trace('setLocalDescription::postTransform', dumpSDP(localDescription));
|
|
|
2034
|
+ localDescription = this._mungeDescription(localDescription);
|
|
2199
|
2035
|
|
|
2200
|
2036
|
return new Promise((resolve, reject) => {
|
|
2201
|
2037
|
this.peerconnection.setLocalDescription(localDescription)
|
|
|
@@ -2219,14 +2055,15 @@ TraceablePeerConnection.prototype.setLocalDescription = function(description) {
|
|
2219
|
2055
|
});
|
|
2220
|
2056
|
};
|
|
2221
|
2057
|
|
|
|
2058
|
+/**
|
|
|
2059
|
+ * Sets the remote description on the peerconnection.
|
|
|
2060
|
+ *
|
|
|
2061
|
+ * @param {RTCSessionDescription} description - The remote description to be set.
|
|
|
2062
|
+ * @returns {Promise<void>} - Resolved when the operation is successful and rejected with an error otherwise.
|
|
|
2063
|
+ */
|
|
2222
|
2064
|
TraceablePeerConnection.prototype.setRemoteDescription = function(description) {
|
|
2223
|
2065
|
let remoteDescription = description;
|
|
2224
|
2066
|
|
|
2225
|
|
- this.trace('setRemoteDescription::preTransform', dumpSDP(description));
|
|
2226
|
|
-
|
|
2227
|
|
- // Munge stereo flag and opusMaxAverageBitrate based on config.js
|
|
2228
|
|
- remoteDescription = this._mungeOpus(remoteDescription);
|
|
2229
|
|
-
|
|
2230
|
2067
|
if (this.isSpatialScalabilityOn()) {
|
|
2231
|
2068
|
remoteDescription = this.tpcUtils.insertUnifiedPlanSimulcastReceive(remoteDescription);
|
|
2232
|
2069
|
this.trace('setRemoteDescription::postTransform (sim receive)', dumpSDP(remoteDescription));
|
|
|
@@ -2234,11 +2071,7 @@ TraceablePeerConnection.prototype.setRemoteDescription = function(description) {
|
|
2234
|
2071
|
remoteDescription = this.tpcUtils.ensureCorrectOrderOfSsrcs(remoteDescription);
|
|
2235
|
2072
|
this.trace('setRemoteDescription::postTransform (correct ssrc order)', dumpSDP(remoteDescription));
|
|
2236
|
2073
|
|
|
2237
|
|
- // Munge the order of the codecs based on the preferences set through config.js.
|
|
2238
|
|
- remoteDescription = this._mungeCodecOrder(remoteDescription);
|
|
2239
|
|
- remoteDescription = this._setMaxBitrates(remoteDescription);
|
|
2240
|
|
- remoteDescription = this._updateAv1DdHeaders(remoteDescription);
|
|
2241
|
|
- this.trace('setRemoteDescription::postTransform (munge codec order)', dumpSDP(remoteDescription));
|
|
|
2074
|
+ remoteDescription = this._mungeDescription(remoteDescription);
|
|
2242
|
2075
|
|
|
2243
|
2076
|
return new Promise((resolve, reject) => {
|
|
2244
|
2077
|
this.peerconnection.setRemoteDescription(remoteDescription)
|
|
|
@@ -2343,7 +2176,7 @@ TraceablePeerConnection.prototype._updateVideoSenderEncodings = function(frameHe
|
|
2343
|
2176
|
parameters.degradationPreference = preference;
|
|
2344
|
2177
|
|
|
2345
|
2178
|
// Calculate the encodings active state based on the resolution requested by the bridge.
|
|
2346
|
|
- const codecForCamera = preferredCodec ?? this.getConfiguredVideoCodec(localVideoTrack);
|
|
|
2179
|
+ const codecForCamera = preferredCodec ?? this.tpcUtils.getConfiguredVideoCodec(localVideoTrack);
|
|
2347
|
2180
|
const codec = isScreensharingTrack ? this._getPreferredCodecForScreenshare(codecForCamera) : codecForCamera;
|
|
2348
|
2181
|
const activeState = this.tpcUtils.calculateEncodingsActiveState(localVideoTrack, codec, frameHeight);
|
|
2349
|
2182
|
let bitrates = this.tpcUtils.calculateEncodingsBitrates(localVideoTrack, codec, frameHeight);
|
|
|
@@ -2435,6 +2268,46 @@ TraceablePeerConnection.prototype._updateVideoSenderEncodings = function(frameHe
|
|
2435
|
2268
|
});
|
|
2436
|
2269
|
};
|
|
2437
|
2270
|
|
|
|
2271
|
+/**
|
|
|
2272
|
+ * Resumes or suspends media on the peerconnection by setting the active state on RTCRtpEncodingParameters
|
|
|
2273
|
+ * associated with all the senders that have a track attached to it.
|
|
|
2274
|
+ *
|
|
|
2275
|
+ * @param {boolean} enable - whether outgoing media needs to be enabled or disabled.
|
|
|
2276
|
+ * @param {string} mediaType - media type, 'audio' or 'video', if neither is passed, all outgoing media will either
|
|
|
2277
|
+ * be enabled or disabled.
|
|
|
2278
|
+ * @returns {Promise} - A promise that is resolved when the change is succesful on all the senders, rejected
|
|
|
2279
|
+ * otherwise.
|
|
|
2280
|
+ */
|
|
|
2281
|
+TraceablePeerConnection.prototype.setMediaTransferActive = function(enable, mediaType) {
|
|
|
2282
|
+ logger.info(`${this} ${enable ? 'Resuming' : 'Suspending'} media transfer.`);
|
|
|
2283
|
+
|
|
|
2284
|
+ const senders = this.peerconnection.getSenders()
|
|
|
2285
|
+ .filter(s => Boolean(s.track) && (!mediaType || s.track.kind === mediaType));
|
|
|
2286
|
+ const promises = [];
|
|
|
2287
|
+
|
|
|
2288
|
+ for (const sender of senders) {
|
|
|
2289
|
+ if (sender.track.kind === MediaType.VIDEO) {
|
|
|
2290
|
+ promises.push(this._updateVideoSenderParameters(() => this._enableSenderEncodings(sender, enable)));
|
|
|
2291
|
+ } else {
|
|
|
2292
|
+ promises.push(this._enableSenderEncodings(sender, enable));
|
|
|
2293
|
+ }
|
|
|
2294
|
+ }
|
|
|
2295
|
+
|
|
|
2296
|
+ return Promise.allSettled(promises)
|
|
|
2297
|
+ .then(settledResult => {
|
|
|
2298
|
+ const errors = settledResult
|
|
|
2299
|
+ .filter(result => result.status === 'rejected')
|
|
|
2300
|
+ .map(result => result.reason);
|
|
|
2301
|
+
|
|
|
2302
|
+ if (errors.length) {
|
|
|
2303
|
+ return Promise.reject(new Error('Failed to change encodings on the RTCRtpSenders'
|
|
|
2304
|
+ + `${errors.join(' ')}`));
|
|
|
2305
|
+ }
|
|
|
2306
|
+
|
|
|
2307
|
+ return Promise.resolve();
|
|
|
2308
|
+ });
|
|
|
2309
|
+};
|
|
|
2310
|
+
|
|
2438
|
2311
|
/**
|
|
2439
|
2312
|
* Enables/disables outgoing video media transmission on this peer connection. When disabled the stream encoding's
|
|
2440
|
2313
|
* active state is enabled or disabled to send or stop the media.
|
|
|
@@ -2450,7 +2323,7 @@ TraceablePeerConnection.prototype.setVideoTransferActive = function(active) {
|
|
2450
|
2323
|
this.videoTransferActive = active;
|
|
2451
|
2324
|
|
|
2452
|
2325
|
if (changed) {
|
|
2453
|
|
- return this.tpcUtils.setMediaTransferActive(active, MediaType.VIDEO);
|
|
|
2326
|
+ return this.setMediaTransferActive(active, MediaType.VIDEO);
|
|
2454
|
2327
|
}
|
|
2455
|
2328
|
|
|
2456
|
2329
|
return Promise.resolve();
|