@webex/web-client-media-engine 3.10.1 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +117 -35
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +117 -35
- package/dist/esm/index.js.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/package.json +3 -3
package/dist/esm/index.js
CHANGED
|
@@ -486,6 +486,7 @@ var WebrtcCoreErrorType;
|
|
|
486
486
|
(function (WebrtcCoreErrorType) {
|
|
487
487
|
WebrtcCoreErrorType["DEVICE_PERMISSION_DENIED"] = "DEVICE_PERMISSION_DENIED";
|
|
488
488
|
WebrtcCoreErrorType["CREATE_STREAM_FAILED"] = "CREATE_STREAM_FAILED";
|
|
489
|
+
WebrtcCoreErrorType["ADD_EFFECT_FAILED"] = "ADD_EFFECT_FAILED";
|
|
489
490
|
})(WebrtcCoreErrorType || (WebrtcCoreErrorType = {}));
|
|
490
491
|
/**
|
|
491
492
|
* Represents a WebRTC core error, which contains error type and error message.
|
|
@@ -1246,11 +1247,12 @@ class _Stream {
|
|
|
1246
1247
|
_a$1$1 = StreamEventNames.MuteStateChange, _b$1 = StreamEventNames.Ended;
|
|
1247
1248
|
const Stream = AddEvents(_Stream);
|
|
1248
1249
|
|
|
1249
|
-
var _a$6, _b;
|
|
1250
|
+
var _a$6, _b, _c;
|
|
1250
1251
|
var LocalStreamEventNames;
|
|
1251
1252
|
(function (LocalStreamEventNames) {
|
|
1252
1253
|
LocalStreamEventNames["ConstraintsChange"] = "constraints-change";
|
|
1253
1254
|
LocalStreamEventNames["OutputTrackChange"] = "output-track-change";
|
|
1255
|
+
LocalStreamEventNames["EffectAdded"] = "effect-added";
|
|
1254
1256
|
})(LocalStreamEventNames || (LocalStreamEventNames = {}));
|
|
1255
1257
|
/**
|
|
1256
1258
|
* A stream which originates on the local device.
|
|
@@ -1265,6 +1267,7 @@ class _LocalStream extends Stream {
|
|
|
1265
1267
|
super(stream);
|
|
1266
1268
|
this[_a$6] = new TypedEvent$1();
|
|
1267
1269
|
this[_b] = new TypedEvent$1();
|
|
1270
|
+
this[_c] = new TypedEvent$1();
|
|
1268
1271
|
this.effects = [];
|
|
1269
1272
|
this.loadingEffects = new Map();
|
|
1270
1273
|
this.inputStream = stream;
|
|
@@ -1356,55 +1359,110 @@ class _LocalStream extends Stream {
|
|
|
1356
1359
|
/**
|
|
1357
1360
|
* Adds an effect to a local stream.
|
|
1358
1361
|
*
|
|
1359
|
-
* @param name - The name of the effect.
|
|
1360
1362
|
* @param effect - The effect to add.
|
|
1361
1363
|
*/
|
|
1362
|
-
addEffect(
|
|
1364
|
+
addEffect(effect) {
|
|
1363
1365
|
return __awaiter$2(this, void 0, void 0, function* () {
|
|
1364
|
-
//
|
|
1365
|
-
this.
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1366
|
+
// Check if the effect has already been added.
|
|
1367
|
+
if (this.effects.some((e) => e.id === effect.id)) {
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
// Load the effect. Because loading is asynchronous, keep track of the loading effects.
|
|
1371
|
+
this.loadingEffects.set(effect.kind, effect);
|
|
1372
|
+
yield effect.load(this.outputTrack);
|
|
1373
|
+
// After loading, check whether or not we still want to use this effect. If another effect of
|
|
1374
|
+
// the same kind was added while this effect was loading, we only want to use the latest effect,
|
|
1375
|
+
// so dispose this one. If the effects list was cleared while this effect was loading, also
|
|
1376
|
+
// dispose it.
|
|
1377
|
+
if (effect !== this.loadingEffects.get(effect.kind)) {
|
|
1369
1378
|
yield effect.dispose();
|
|
1370
|
-
throw new
|
|
1379
|
+
throw new WebrtcCoreError(WebrtcCoreErrorType.ADD_EFFECT_FAILED, `Another effect with kind ${effect.kind} was added while effect ${effect.id} was loading, or the effects list was cleared.`);
|
|
1371
1380
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
const effectIndex = this.effects.findIndex((e) => e.name === name);
|
|
1381
|
+
this.loadingEffects.delete(effect.kind);
|
|
1382
|
+
/**
|
|
1383
|
+
* Handle when the effect's output track has been changed. This will update the input of the
|
|
1384
|
+
* next effect in the effects list of the output of the stream.
|
|
1385
|
+
*
|
|
1386
|
+
* @param track - The new output track of the effect.
|
|
1387
|
+
*/
|
|
1388
|
+
const handleEffectTrackUpdated = (track) => {
|
|
1389
|
+
var _d;
|
|
1390
|
+
const effectIndex = this.effects.findIndex((e) => e.id === effect.id);
|
|
1383
1391
|
if (effectIndex === this.effects.length - 1) {
|
|
1384
1392
|
this.changeOutputTrack(track);
|
|
1385
1393
|
}
|
|
1394
|
+
else if (effectIndex >= 0) {
|
|
1395
|
+
(_d = this.effects[effectIndex + 1]) === null || _d === void 0 ? void 0 : _d.replaceInputTrack(track);
|
|
1396
|
+
}
|
|
1386
1397
|
else {
|
|
1387
|
-
(
|
|
1398
|
+
logger$3.error(`Effect with ID ${effect.id} not found in effects list.`);
|
|
1388
1399
|
}
|
|
1389
|
-
}
|
|
1400
|
+
};
|
|
1401
|
+
/**
|
|
1402
|
+
* Handle when the effect has been disposed. This will remove all event listeners from the
|
|
1403
|
+
* effect.
|
|
1404
|
+
*/
|
|
1405
|
+
const handleEffectDisposed = () => {
|
|
1406
|
+
effect.off('track-updated', handleEffectTrackUpdated);
|
|
1407
|
+
effect.off('disposed', handleEffectDisposed);
|
|
1408
|
+
};
|
|
1409
|
+
// TODO: using EffectEvent.TrackUpdated or EffectEvent.Disposed will cause the entire
|
|
1410
|
+
// web-media-effects lib to be rebuilt and inflates the size of the webrtc-core build, so
|
|
1411
|
+
// we use type assertion here as a temporary workaround.
|
|
1412
|
+
effect.on('track-updated', handleEffectTrackUpdated);
|
|
1413
|
+
effect.on('disposed', handleEffectDisposed);
|
|
1414
|
+
// Add the effect to the effects list. If an effect of the same kind has already been added,
|
|
1415
|
+
// dispose the existing effect and replace it with the new effect. If the existing effect was
|
|
1416
|
+
// enabled, also enable the new effect.
|
|
1417
|
+
const existingEffectIndex = this.effects.findIndex((e) => e.kind === effect.kind);
|
|
1418
|
+
if (existingEffectIndex >= 0) {
|
|
1419
|
+
const [existingEffect] = this.effects.splice(existingEffectIndex, 1, effect);
|
|
1420
|
+
if (existingEffect.isEnabled) {
|
|
1421
|
+
// If the existing effect is not the first effect in the effects list, then the input of the
|
|
1422
|
+
// new effect should be the output of the previous effect in the effects list. We know the
|
|
1423
|
+
// output track of the previous effect must exist because it must have been loaded (and all
|
|
1424
|
+
// loaded effects have an output track).
|
|
1425
|
+
const inputTrack = existingEffectIndex === 0
|
|
1426
|
+
? this.inputTrack
|
|
1427
|
+
: this.effects[existingEffectIndex - 1].getOutputTrack();
|
|
1428
|
+
yield effect.replaceInputTrack(inputTrack);
|
|
1429
|
+
// Enabling the new effect will trigger the track-updated event, which will handle the new
|
|
1430
|
+
// effect's updated output track.
|
|
1431
|
+
yield effect.enable();
|
|
1432
|
+
}
|
|
1433
|
+
yield existingEffect.dispose();
|
|
1434
|
+
}
|
|
1435
|
+
else {
|
|
1436
|
+
this.effects.push(effect);
|
|
1437
|
+
}
|
|
1438
|
+
// Emit an event with the effect so others can listen to the effect events.
|
|
1439
|
+
this[LocalStreamEventNames.EffectAdded].emit(effect);
|
|
1390
1440
|
});
|
|
1391
1441
|
}
|
|
1392
1442
|
/**
|
|
1393
|
-
* Get an effect from the effects list.
|
|
1443
|
+
* Get an effect from the effects list by ID.
|
|
1394
1444
|
*
|
|
1395
|
-
* @param
|
|
1445
|
+
* @param id - The id of the effect you want to get.
|
|
1396
1446
|
* @returns The effect or undefined.
|
|
1397
1447
|
*/
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1448
|
+
getEffectById(id) {
|
|
1449
|
+
return this.effects.find((effect) => effect.id === id);
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Get an effect from the effects list by kind.
|
|
1453
|
+
*
|
|
1454
|
+
* @param kind - The kind of the effect you want to get.
|
|
1455
|
+
* @returns The effect or undefined.
|
|
1456
|
+
*/
|
|
1457
|
+
getEffectByKind(kind) {
|
|
1458
|
+
return this.effects.find((effect) => effect.kind === kind);
|
|
1401
1459
|
}
|
|
1402
1460
|
/**
|
|
1403
1461
|
* Get all the effects from the effects list.
|
|
1404
1462
|
*
|
|
1405
|
-
* @returns A list of
|
|
1463
|
+
* @returns A list of effects.
|
|
1406
1464
|
*/
|
|
1407
|
-
|
|
1465
|
+
getEffects() {
|
|
1408
1466
|
return this.effects;
|
|
1409
1467
|
}
|
|
1410
1468
|
/**
|
|
@@ -1416,13 +1474,13 @@ class _LocalStream extends Stream {
|
|
|
1416
1474
|
// Dispose of any effects currently in use
|
|
1417
1475
|
if (this.effects.length > 0) {
|
|
1418
1476
|
this.changeOutputTrack(this.inputTrack);
|
|
1419
|
-
yield Promise.all(this.effects.map((
|
|
1477
|
+
yield Promise.all(this.effects.map((effect) => effect.dispose()));
|
|
1420
1478
|
this.effects = [];
|
|
1421
1479
|
}
|
|
1422
1480
|
});
|
|
1423
1481
|
}
|
|
1424
1482
|
}
|
|
1425
|
-
_a$6 = LocalStreamEventNames.ConstraintsChange, _b = LocalStreamEventNames.OutputTrackChange;
|
|
1483
|
+
_a$6 = LocalStreamEventNames.ConstraintsChange, _b = LocalStreamEventNames.OutputTrackChange, _c = LocalStreamEventNames.EffectAdded;
|
|
1426
1484
|
const LocalStream = AddEvents(_LocalStream);
|
|
1427
1485
|
|
|
1428
1486
|
/**
|
|
@@ -9034,6 +9092,18 @@ function parse(sdp, grammar = DefaultSdpGrammar) {
|
|
|
9034
9092
|
const parsed = parseToModel(lines);
|
|
9035
9093
|
return parsed;
|
|
9036
9094
|
}
|
|
9095
|
+
|
|
9096
|
+
function disableRtcpFbValue(sdpOrAv, rtcpFbValue) {
|
|
9097
|
+
const mediaDescriptions = sdpOrAv instanceof Sdp ? sdpOrAv.avMedia : [sdpOrAv];
|
|
9098
|
+
mediaDescriptions.forEach((media) => {
|
|
9099
|
+
media.codecs.forEach((codec) => {
|
|
9100
|
+
codec.feedback = codec.feedback.filter((fb) => fb !== rtcpFbValue);
|
|
9101
|
+
});
|
|
9102
|
+
});
|
|
9103
|
+
}
|
|
9104
|
+
function disableTwcc(sdpOrAv) {
|
|
9105
|
+
disableRtcpFbValue(sdpOrAv, 'transport-cc');
|
|
9106
|
+
}
|
|
9037
9107
|
function removeCodec(sdpOrAv, codecName) {
|
|
9038
9108
|
const mediaDescriptions = sdpOrAv instanceof Sdp ? sdpOrAv.avMedia : [sdpOrAv];
|
|
9039
9109
|
mediaDescriptions.forEach((media) => {
|
|
@@ -9375,7 +9445,7 @@ class EgressSdpMunger {
|
|
|
9375
9445
|
reset() {
|
|
9376
9446
|
this.streamIds = [];
|
|
9377
9447
|
}
|
|
9378
|
-
mungeLocalDescription(mediaDescription, simulcastEnabled, rtxEnabled) {
|
|
9448
|
+
mungeLocalDescription(mediaDescription, simulcastEnabled, rtxEnabled, twccDisabled) {
|
|
9379
9449
|
var _a;
|
|
9380
9450
|
retainCodecs(mediaDescription, ['h264', 'opus', 'rtx']);
|
|
9381
9451
|
if (mediaDescription.codecs.size === 0) {
|
|
@@ -9453,6 +9523,9 @@ class EgressSdpMunger {
|
|
|
9453
9523
|
mediaDescription.addLine(new SsrcGroupLine('SIM', this.streamIds.map((streamId) => streamId.ssrc)));
|
|
9454
9524
|
}
|
|
9455
9525
|
applyFormatParameters(mediaDescription, this.customCodecParameters);
|
|
9526
|
+
if (twccDisabled) {
|
|
9527
|
+
disableTwcc(mediaDescription);
|
|
9528
|
+
}
|
|
9456
9529
|
}
|
|
9457
9530
|
mungeLocalDescriptionForRemoteServer(mediaDescription, mediaContent, csi) {
|
|
9458
9531
|
injectContentType(mediaDescription, mediaContent);
|
|
@@ -9979,13 +10052,16 @@ class IngressSdpMunger {
|
|
|
9979
10052
|
getReceiverId() {
|
|
9980
10053
|
return Object.assign({ ssrc: this.ssrc }, (this.rtxSsrc ? { rtxSsrc: this.rtxSsrc } : {}));
|
|
9981
10054
|
}
|
|
9982
|
-
mungeLocalDescription(mediaDescription) {
|
|
10055
|
+
mungeLocalDescription(mediaDescription, twccDisabled) {
|
|
9983
10056
|
retainCodecs(mediaDescription, ['h264', 'opus', 'rtx']);
|
|
9984
10057
|
if (mediaDescription.codecs.size === 0) {
|
|
9985
10058
|
logErrorAndThrow(WcmeErrorType.SDP_MUNGE_MISSING_CODECS, `No codecs present in m-line with MID ${mediaDescription.mid} after filtering.`);
|
|
9986
10059
|
}
|
|
9987
10060
|
mediaDescription.bandwidth = new BandwidthLine('TIAS', 20000000);
|
|
9988
10061
|
removeMidRidExtensions(mediaDescription);
|
|
10062
|
+
if (twccDisabled) {
|
|
10063
|
+
disableTwcc(mediaDescription);
|
|
10064
|
+
}
|
|
9989
10065
|
}
|
|
9990
10066
|
mungeRemoteDescription(mediaDescription) {
|
|
9991
10067
|
if (!mediaDescription.ssrcs.length) {
|
|
@@ -10135,6 +10211,7 @@ ReceiveSlot.Events = ReceiveSlotEvents;
|
|
|
10135
10211
|
|
|
10136
10212
|
class Transceiver {
|
|
10137
10213
|
constructor(rtcRtpTransceiver, mid) {
|
|
10214
|
+
this.twccDisabled = false;
|
|
10138
10215
|
this._rtcRtpTransceiver = rtcRtpTransceiver;
|
|
10139
10216
|
this.mid = mid;
|
|
10140
10217
|
}
|
|
@@ -10178,7 +10255,7 @@ class ReceiveOnlyTransceiver extends Transceiver {
|
|
|
10178
10255
|
return this.receiver.getStats();
|
|
10179
10256
|
}
|
|
10180
10257
|
mungeLocalDescription(mediaDescription) {
|
|
10181
|
-
this.munger.mungeLocalDescription(mediaDescription);
|
|
10258
|
+
this.munger.mungeLocalDescription(mediaDescription, this.twccDisabled);
|
|
10182
10259
|
}
|
|
10183
10260
|
mungeRemoteDescription(mediaDescription) {
|
|
10184
10261
|
this.munger.mungeRemoteDescription(mediaDescription);
|
|
@@ -14041,7 +14118,7 @@ class SendOnlyTransceiver extends Transceiver {
|
|
|
14041
14118
|
return params.encodings.length > 1;
|
|
14042
14119
|
}
|
|
14043
14120
|
mungeLocalDescription(mediaDescription) {
|
|
14044
|
-
this.munger.mungeLocalDescription(mediaDescription, this.isSimulcastEnabled(), this.rtxEnabled);
|
|
14121
|
+
this.munger.mungeLocalDescription(mediaDescription, this.isSimulcastEnabled(), this.rtxEnabled, this.twccDisabled);
|
|
14045
14122
|
}
|
|
14046
14123
|
mungeLocalDescriptionForRemoteServer(mediaDescription) {
|
|
14047
14124
|
this.munger.mungeLocalDescriptionForRemoteServer(mediaDescription, getMediaContent(this.mediaType), this.csi);
|
|
@@ -14227,6 +14304,7 @@ const defaultMultistreamConnectionOptions = {
|
|
|
14227
14304
|
bundlePolicy: 'max-compat',
|
|
14228
14305
|
iceServers: undefined,
|
|
14229
14306
|
disableContentSimulcast: true,
|
|
14307
|
+
disableAudioTwcc: true,
|
|
14230
14308
|
};
|
|
14231
14309
|
class MultistreamConnection extends EventEmitter$2 {
|
|
14232
14310
|
constructor(userOptions = {}) {
|
|
@@ -14304,6 +14382,8 @@ class MultistreamConnection extends EventEmitter$2 {
|
|
|
14304
14382
|
'max-fs': `${defaultMaxVideoEncodeFrameSize}`,
|
|
14305
14383
|
});
|
|
14306
14384
|
}
|
|
14385
|
+
transceiver.twccDisabled =
|
|
14386
|
+
getMediaFamily(mediaType) === MediaFamily.Audio ? this.options.disableAudioTwcc : false;
|
|
14307
14387
|
transceiver.active = false;
|
|
14308
14388
|
transceiver.streamMuteStateChange.on(() => {
|
|
14309
14389
|
this.sendSourceAdvertisement(mediaType);
|
|
@@ -14564,6 +14644,8 @@ class MultistreamConnection extends EventEmitter$2 {
|
|
|
14564
14644
|
const transceiverMid = this.midPredictor.getNextMid(mediaType);
|
|
14565
14645
|
const munger = new IngressSdpMunger();
|
|
14566
14646
|
const recvOnlyTransceiver = new ReceiveOnlyTransceiver(rtcRtpTransceiver, transceiverMid, munger);
|
|
14647
|
+
recvOnlyTransceiver.twccDisabled =
|
|
14648
|
+
getMediaFamily(mediaType) === MediaFamily.Audio ? this.options.disableAudioTwcc : false;
|
|
14567
14649
|
this.recvTransceivers.set(mediaType, [
|
|
14568
14650
|
...(this.recvTransceivers.get(mediaType) || []),
|
|
14569
14651
|
recvOnlyTransceiver,
|