@webex/web-client-media-engine 1.38.4 → 1.40.0

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 CHANGED
@@ -978,6 +978,10 @@ exports.LocalTrackEvents = void 0;
978
978
  * Fires when there has been a change in the underlying track.
979
979
  */
980
980
  LocalTrackEvents["UnderlyingTrackChange"] = "underlying-track-change";
981
+ /**
982
+ * Fires when the applyConstraints() has been called for the track.
983
+ */
984
+ LocalTrackEvents["TrackConstraintsChange"] = "track-constraints-change";
981
985
  })(exports.LocalTrackEvents || (exports.LocalTrackEvents = {}));
982
986
  // TBD: Fix this once types are published separately
983
987
  // export type TrackEffect = BaseMicrophoneEffect | BaseCameraEffect;
@@ -1151,7 +1155,7 @@ class LocalTrack extends EventEmitter$2 {
1151
1155
  return effect;
1152
1156
  }
1153
1157
  /**
1154
- * Cleanup local microphone track.
1158
+ * Cleanup the local microphone track.
1155
1159
  */
1156
1160
  disposeEffects() {
1157
1161
  if (this.effects.size > 0) {
@@ -1161,6 +1165,59 @@ class LocalTrack extends EventEmitter$2 {
1161
1165
  this.emit(exports.LocalTrackEvents.UnderlyingTrackChange);
1162
1166
  }
1163
1167
  }
1168
+ /**
1169
+ * Apply constraints to the track.
1170
+ *
1171
+ * @param constraints - The constraints to apply to the track.
1172
+ * @returns A promise which resolves when the constraints have been successfully applied.
1173
+ */
1174
+ applyConstraints(constraints) {
1175
+ return __awaiter$1(this, void 0, void 0, function* () {
1176
+ logger$3.log(`Applying constraints to local track:`, constraints);
1177
+ const ret = this.underlyingTrack.applyConstraints(constraints).then(() => {
1178
+ this.emit(exports.LocalTrackEvents.TrackConstraintsChange);
1179
+ });
1180
+ return ret;
1181
+ });
1182
+ }
1183
+ /**
1184
+ * Get the current constraints of the track.
1185
+ *
1186
+ * @returns The constraints of the track.
1187
+ */
1188
+ getConstraints() {
1189
+ return this.underlyingTrack.getConstraints();
1190
+ }
1191
+ /**
1192
+ * Get the current settings of the track.
1193
+ *
1194
+ * @returns The settings of the track.
1195
+ */
1196
+ getSettings() {
1197
+ return this.underlyingTrack.getSettings();
1198
+ }
1199
+ /**
1200
+ * Check the resolution and then return how many layers will be active.
1201
+ *
1202
+ * @returns The active layers count.
1203
+ */
1204
+ getNumActiveSimulcastLayers() {
1205
+ let activeSimulcastLayersNumber = 0;
1206
+ if (this.trackState.type === 'audio') {
1207
+ return activeSimulcastLayersNumber;
1208
+ }
1209
+ const videoHeight = this.underlyingTrack.getSettings().height;
1210
+ if (videoHeight <= 180) {
1211
+ activeSimulcastLayersNumber = 1;
1212
+ }
1213
+ else if (videoHeight <= 360) {
1214
+ activeSimulcastLayersNumber = 2;
1215
+ }
1216
+ else {
1217
+ activeSimulcastLayersNumber = 3;
1218
+ }
1219
+ return activeSimulcastLayersNumber;
1220
+ }
1164
1221
  }
1165
1222
  LocalTrack.Events = exports.LocalTrackEvents;
1166
1223
 
@@ -1204,7 +1261,10 @@ class WcmeError {
1204
1261
  }
1205
1262
  }
1206
1263
  /**
1207
- * Creates a camera video track.
1264
+ * Creates a camera video track. Please note that the constraint params in second getUserMedia call would NOT take effect when:
1265
+ *
1266
+ * 1. Previous captured video track from the same device is not stopped .
1267
+ * 2. Previous createCameraTrack() call for the same device is in progress.
1208
1268
  *
1209
1269
  * @param constraints - Video device constraints.
1210
1270
  * @returns A LocalTrack object or an error.
@@ -4995,6 +5055,44 @@ class PeerConnection extends EventEmitter$2 {
4995
5055
  get iceGatheringState() {
4996
5056
  return this.pc.iceGatheringState;
4997
5057
  }
5058
+ /**
5059
+ * Returns the type of a connection that has been established.
5060
+ *
5061
+ * @returns The connection type which would be `ConnectionType`.
5062
+ */
5063
+ getCurrentConnectionType() {
5064
+ var _a;
5065
+ return __awaiter$1(this, void 0, void 0, function* () {
5066
+ // make sure this method only can be called when the ice connection is established;
5067
+ const isIceConnected = this.pc.iceConnectionState === 'connected' || this.pc.iceConnectionState === 'completed';
5068
+ if (!isIceConnected) {
5069
+ throw new Error('Ice connection is not established');
5070
+ }
5071
+ const succeededLocalCandidateIds = new Set();
5072
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5073
+ const localCandidateStatsReports = [];
5074
+ (yield this.pc.getStats()).forEach((report) => {
5075
+ var _a;
5076
+ // collect all local candidate ids from `candidate-pair` stats reports with `succeeded` state.
5077
+ if (report.type === 'candidate-pair' && ((_a = report.state) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'succeeded') {
5078
+ succeededLocalCandidateIds.add(report.localCandidateId);
5079
+ }
5080
+ // collect all `local-candidate` stats.
5081
+ if (report.type === 'local-candidate') {
5082
+ localCandidateStatsReports.push(report);
5083
+ }
5084
+ });
5085
+ // find the `local-candidate` stats which report id contains in `succeededLocalCandidateIds`.
5086
+ const localCandidate = localCandidateStatsReports.find((report) => succeededLocalCandidateIds.has(report.id));
5087
+ if (!localCandidate) {
5088
+ return 'unknown';
5089
+ }
5090
+ if (localCandidate.relayProtocol) {
5091
+ return `TURN-${localCandidate.relayProtocol.toUpperCase()}`;
5092
+ }
5093
+ return (_a = localCandidate.protocol) === null || _a === void 0 ? void 0 : _a.toUpperCase();
5094
+ });
5095
+ }
4998
5096
  }
4999
5097
  PeerConnection.Events = PeerConnectionEvents;
5000
5098
 
@@ -8864,7 +8962,7 @@ function injectContentTypes(sdp, contentTypeMap) {
8864
8962
  }
8865
8963
  function injectJmpAttributes(parsedSdp, csiMap, streamSignalingMode) {
8866
8964
  parsedSdp.avMedia
8867
- .filter((mLine) => mLine.direction === 'sendrecv' || mLine.direction === 'sendonly')
8965
+ .filter((mLine) => mLine.direction === 'sendrecv' || mLine.direction === 'inactive')
8868
8966
  .forEach((mLine) => {
8869
8967
  if (!mLine.otherLines.find((line) => line instanceof JmpLine)) {
8870
8968
  mLine.addLine(new JmpLine());
@@ -8888,6 +8986,7 @@ class SendOnlyTransceiver extends Transceiver {
8888
8986
  super(rtcpRtpTransceiver);
8889
8987
  this.requested = false;
8890
8988
  this.csi = csi;
8989
+ this.direction = 'sendrecv';
8891
8990
  this.handleTrackChange = this.handleTrackChange.bind(this);
8892
8991
  }
8893
8992
  handleTrackChange() {
@@ -8905,6 +9004,7 @@ class SendOnlyTransceiver extends Transceiver {
8905
9004
  var _a;
8906
9005
  return __awaiter(this, void 0, void 0, function* () {
8907
9006
  _super.replaceTransceiver.call(this, newRtcRtpTransceiver);
9007
+ newRtcRtpTransceiver.direction = this.direction;
8908
9008
  if (this.requested) {
8909
9009
  yield this.sender.replaceTrack(((_a = this.publishedTrack) === null || _a === void 0 ? void 0 : _a.underlyingTrack) || null);
8910
9010
  }
@@ -8975,8 +9075,8 @@ class SendOnlyTransceiver extends Transceiver {
8975
9075
  return this.replacePublishedTrack();
8976
9076
  }
8977
9077
  setActive(enabled) {
8978
- const direction = enabled ? 'sendrecv' : 'inactive';
8979
- this._rtcRtpTransceiver.direction = direction;
9078
+ this.direction = enabled ? 'sendrecv' : 'inactive';
9079
+ this._rtcRtpTransceiver.direction = this.direction;
8980
9080
  return this._rtcRtpTransceiver.direction !== this._rtcRtpTransceiver.currentDirection;
8981
9081
  }
8982
9082
  getStats() {
@@ -9599,9 +9699,16 @@ const defaultMultistreamConnectionOptions = {
9599
9699
  bundlePolicy: 'max-compat',
9600
9700
  iceServers: undefined,
9601
9701
  disableContentSimulcast: true,
9702
+ enableMainAudio: true,
9703
+ enableMainVideo: true,
9704
+ };
9705
+ const defaultVideoCodecParameters = {
9706
+ 'max-mbps': `${defaultMaxVideoEncodeMbps}`,
9707
+ 'max-fs': `${defaultMaxVideoEncodeFrameSize}`,
9602
9708
  };
9603
9709
  class MultistreamConnection extends EventEmitter {
9604
9710
  constructor(userOptions = {}) {
9711
+ var _a, _b;
9605
9712
  super();
9606
9713
  this.sendTransceivers = new Map();
9607
9714
  this.recvTransceivers = new Map();
@@ -9609,6 +9716,7 @@ class MultistreamConnection extends EventEmitter {
9609
9716
  this.pendingJmpTasks = [];
9610
9717
  this.metricsCallback = () => { };
9611
9718
  this.overuseUpdateCallback = () => { };
9719
+ this.customCodecParameters = new Map();
9612
9720
  this.midMap = new Map();
9613
9721
  this.currentMid = 0;
9614
9722
  this.options = Object.assign(Object.assign({}, defaultMultistreamConnectionOptions), userOptions);
@@ -9621,11 +9729,15 @@ class MultistreamConnection extends EventEmitter {
9621
9729
  const mainSceneId = generateSceneId();
9622
9730
  const videoMainEncodingOptions = this.getVideoEncodingOptions(MediaContent.Main);
9623
9731
  this.createSendTransceiver(MediaType.VideoMain, mainSceneId, videoMainEncodingOptions);
9732
+ this.setCodecParameters(MediaType.VideoMain, defaultVideoCodecParameters);
9624
9733
  this.createSendTransceiver(MediaType.AudioMain, mainSceneId);
9734
+ (_a = this.sendTransceivers.get(MediaType.VideoMain)) === null || _a === void 0 ? void 0 : _a.setActive(this.options.enableMainVideo);
9735
+ (_b = this.sendTransceivers.get(MediaType.AudioMain)) === null || _b === void 0 ? void 0 : _b.setActive(this.options.enableMainAudio);
9625
9736
  if (this.options.floorControlledPresentation) {
9626
9737
  const videoPresentationEncodingOptions = this.getVideoEncodingOptions(MediaContent.Slides);
9627
9738
  const contentSceneId = generateSceneId();
9628
9739
  this.createSendTransceiver(MediaType.VideoSlides, contentSceneId, videoPresentationEncodingOptions);
9740
+ this.setCodecParameters(MediaType.VideoSlides, defaultVideoCodecParameters);
9629
9741
  this.createSendTransceiver(MediaType.AudioSlides, contentSceneId);
9630
9742
  }
9631
9743
  }
@@ -9990,20 +10102,7 @@ class MultistreamConnection extends EventEmitter {
9990
10102
  return ingressSignaler.getReceiverId();
9991
10103
  });
9992
10104
  if (this.pc.getRemoteDescription()) {
9993
- yield this.pc
9994
- .createOffer()
9995
- .then((offer) => {
9996
- if (!offer.sdp) {
9997
- throw new Error('No SDP offer');
9998
- }
9999
- offer.sdp = this.preProcessLocalOffer(offer.sdp);
10000
- return this.pc.setLocalDescription(offer);
10001
- })
10002
- .then(() => {
10003
- var _a;
10004
- const answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
10005
- return this.pc.setRemoteDescription({ type: 'answer', sdp: answer });
10006
- });
10105
+ yield this.doLocalOfferAnswer();
10007
10106
  }
10008
10107
  this.recvTransceivers.set(mediaType, [
10009
10108
  ...(this.recvTransceivers.get(mediaType) || []),
@@ -10069,8 +10168,26 @@ class MultistreamConnection extends EventEmitter {
10069
10168
  }
10070
10169
  setAnswer(answer) {
10071
10170
  return __awaiter(this, void 0, void 0, function* () {
10171
+ const isInitialAnswer = !this.pc.getRemoteDescription();
10072
10172
  const sdp = this.preProcessRemoteAnswer(answer);
10073
- return this.pc.setRemoteDescription({ type: 'answer', sdp });
10173
+ return this.pc.setRemoteDescription({ type: 'answer', sdp }).then(() => {
10174
+ if (isInitialAnswer && this.customCodecParameters.size > 0) {
10175
+ this.doLocalOfferAnswer();
10176
+ }
10177
+ });
10178
+ });
10179
+ }
10180
+ doLocalOfferAnswer() {
10181
+ var _a;
10182
+ return __awaiter(this, void 0, void 0, function* () {
10183
+ const offer = yield this.pc.createOffer();
10184
+ if (!offer.sdp) {
10185
+ throw new Error('No SDP offer');
10186
+ }
10187
+ offer.sdp = this.preProcessLocalOffer(offer.sdp);
10188
+ yield this.pc.setLocalDescription(offer);
10189
+ const answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
10190
+ return this.pc.setRemoteDescription({ type: 'answer', sdp: answer });
10074
10191
  });
10075
10192
  }
10076
10193
  enableMultistreamAudio(enabled) {
@@ -10097,12 +10214,20 @@ class MultistreamConnection extends EventEmitter {
10097
10214
  const simulcastEnabled = !!av.simulcast;
10098
10215
  const rtxEnabled = av.type === 'video';
10099
10216
  egressSignaler.signalStreams(simulcastEnabled, rtxEnabled, av);
10100
- if (av.type === 'video') {
10217
+ const mediaType = [...this.sendTransceivers.keys()].find((key) => { var _a; return ((_a = this.sendTransceivers.get(key)) === null || _a === void 0 ? void 0 : _a.mid) === av.mid; });
10218
+ if (mediaType && this.customCodecParameters.has(mediaType)) {
10101
10219
  [...av.codecs.values()]
10102
- .filter((ci) => ci.name === 'H264')
10220
+ .filter((ci) => ci.name === (av.type === 'audio' ? 'opus' : 'H264'))
10103
10221
  .forEach((ci) => {
10104
- ci.fmtParams.set('max-mbps', `${defaultMaxVideoEncodeMbps}`);
10105
- ci.fmtParams.set('max-fs', `${defaultMaxVideoEncodeFrameSize}`);
10222
+ var _a;
10223
+ (_a = this.customCodecParameters.get(mediaType)) === null || _a === void 0 ? void 0 : _a.forEach((value, param) => {
10224
+ if (value === null) {
10225
+ ci.fmtParams.delete(param);
10226
+ }
10227
+ else {
10228
+ ci.fmtParams.set(param, `${value}`);
10229
+ }
10230
+ });
10106
10231
  });
10107
10232
  }
10108
10233
  });
@@ -10158,6 +10283,30 @@ class MultistreamConnection extends EventEmitter {
10158
10283
  .map((transceiver) => transceiver.publishedTrack)
10159
10284
  .filter(Boolean);
10160
10285
  }
10286
+ setCodecParameters(mediaType, parameters) {
10287
+ return __awaiter(this, void 0, void 0, function* () {
10288
+ const currentParams = this.customCodecParameters.get(mediaType) || new Map();
10289
+ Object.entries(parameters).forEach(([param, value]) => {
10290
+ currentParams.set(param, value);
10291
+ });
10292
+ this.customCodecParameters.set(mediaType, currentParams);
10293
+ if (this.pc.getRemoteDescription()) {
10294
+ yield this.doLocalOfferAnswer();
10295
+ }
10296
+ });
10297
+ }
10298
+ deleteCodecParameters(mediaType, parameters) {
10299
+ return __awaiter(this, void 0, void 0, function* () {
10300
+ const currentParams = this.customCodecParameters.get(mediaType) || new Map();
10301
+ parameters.forEach((param) => {
10302
+ currentParams.set(param, null);
10303
+ });
10304
+ this.customCodecParameters.set(mediaType, currentParams);
10305
+ if (this.pc.getRemoteDescription()) {
10306
+ yield this.doLocalOfferAnswer();
10307
+ }
10308
+ });
10309
+ }
10161
10310
  requestMedia(mediaType, mediaRequests) {
10162
10311
  var _a;
10163
10312
  const task = () => {