@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/esm/index.js CHANGED
@@ -974,6 +974,10 @@ var LocalTrackEvents;
974
974
  * Fires when there has been a change in the underlying track.
975
975
  */
976
976
  LocalTrackEvents["UnderlyingTrackChange"] = "underlying-track-change";
977
+ /**
978
+ * Fires when the applyConstraints() has been called for the track.
979
+ */
980
+ LocalTrackEvents["TrackConstraintsChange"] = "track-constraints-change";
977
981
  })(LocalTrackEvents || (LocalTrackEvents = {}));
978
982
  // TBD: Fix this once types are published separately
979
983
  // export type TrackEffect = BaseMicrophoneEffect | BaseCameraEffect;
@@ -1147,7 +1151,7 @@ class LocalTrack extends EventEmitter$2 {
1147
1151
  return effect;
1148
1152
  }
1149
1153
  /**
1150
- * Cleanup local microphone track.
1154
+ * Cleanup the local microphone track.
1151
1155
  */
1152
1156
  disposeEffects() {
1153
1157
  if (this.effects.size > 0) {
@@ -1157,6 +1161,59 @@ class LocalTrack extends EventEmitter$2 {
1157
1161
  this.emit(LocalTrackEvents.UnderlyingTrackChange);
1158
1162
  }
1159
1163
  }
1164
+ /**
1165
+ * Apply constraints to the track.
1166
+ *
1167
+ * @param constraints - The constraints to apply to the track.
1168
+ * @returns A promise which resolves when the constraints have been successfully applied.
1169
+ */
1170
+ applyConstraints(constraints) {
1171
+ return __awaiter$1(this, void 0, void 0, function* () {
1172
+ logger$3.log(`Applying constraints to local track:`, constraints);
1173
+ const ret = this.underlyingTrack.applyConstraints(constraints).then(() => {
1174
+ this.emit(LocalTrackEvents.TrackConstraintsChange);
1175
+ });
1176
+ return ret;
1177
+ });
1178
+ }
1179
+ /**
1180
+ * Get the current constraints of the track.
1181
+ *
1182
+ * @returns The constraints of the track.
1183
+ */
1184
+ getConstraints() {
1185
+ return this.underlyingTrack.getConstraints();
1186
+ }
1187
+ /**
1188
+ * Get the current settings of the track.
1189
+ *
1190
+ * @returns The settings of the track.
1191
+ */
1192
+ getSettings() {
1193
+ return this.underlyingTrack.getSettings();
1194
+ }
1195
+ /**
1196
+ * Check the resolution and then return how many layers will be active.
1197
+ *
1198
+ * @returns The active layers count.
1199
+ */
1200
+ getNumActiveSimulcastLayers() {
1201
+ let activeSimulcastLayersNumber = 0;
1202
+ if (this.trackState.type === 'audio') {
1203
+ return activeSimulcastLayersNumber;
1204
+ }
1205
+ const videoHeight = this.underlyingTrack.getSettings().height;
1206
+ if (videoHeight <= 180) {
1207
+ activeSimulcastLayersNumber = 1;
1208
+ }
1209
+ else if (videoHeight <= 360) {
1210
+ activeSimulcastLayersNumber = 2;
1211
+ }
1212
+ else {
1213
+ activeSimulcastLayersNumber = 3;
1214
+ }
1215
+ return activeSimulcastLayersNumber;
1216
+ }
1160
1217
  }
1161
1218
  LocalTrack.Events = LocalTrackEvents;
1162
1219
 
@@ -1200,7 +1257,10 @@ class WcmeError {
1200
1257
  }
1201
1258
  }
1202
1259
  /**
1203
- * Creates a camera video track.
1260
+ * Creates a camera video track. Please note that the constraint params in second getUserMedia call would NOT take effect when:
1261
+ *
1262
+ * 1. Previous captured video track from the same device is not stopped .
1263
+ * 2. Previous createCameraTrack() call for the same device is in progress.
1204
1264
  *
1205
1265
  * @param constraints - Video device constraints.
1206
1266
  * @returns A LocalTrack object or an error.
@@ -4991,6 +5051,44 @@ class PeerConnection extends EventEmitter$2 {
4991
5051
  get iceGatheringState() {
4992
5052
  return this.pc.iceGatheringState;
4993
5053
  }
5054
+ /**
5055
+ * Returns the type of a connection that has been established.
5056
+ *
5057
+ * @returns The connection type which would be `ConnectionType`.
5058
+ */
5059
+ getCurrentConnectionType() {
5060
+ var _a;
5061
+ return __awaiter$1(this, void 0, void 0, function* () {
5062
+ // make sure this method only can be called when the ice connection is established;
5063
+ const isIceConnected = this.pc.iceConnectionState === 'connected' || this.pc.iceConnectionState === 'completed';
5064
+ if (!isIceConnected) {
5065
+ throw new Error('Ice connection is not established');
5066
+ }
5067
+ const succeededLocalCandidateIds = new Set();
5068
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5069
+ const localCandidateStatsReports = [];
5070
+ (yield this.pc.getStats()).forEach((report) => {
5071
+ var _a;
5072
+ // collect all local candidate ids from `candidate-pair` stats reports with `succeeded` state.
5073
+ if (report.type === 'candidate-pair' && ((_a = report.state) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'succeeded') {
5074
+ succeededLocalCandidateIds.add(report.localCandidateId);
5075
+ }
5076
+ // collect all `local-candidate` stats.
5077
+ if (report.type === 'local-candidate') {
5078
+ localCandidateStatsReports.push(report);
5079
+ }
5080
+ });
5081
+ // find the `local-candidate` stats which report id contains in `succeededLocalCandidateIds`.
5082
+ const localCandidate = localCandidateStatsReports.find((report) => succeededLocalCandidateIds.has(report.id));
5083
+ if (!localCandidate) {
5084
+ return 'unknown';
5085
+ }
5086
+ if (localCandidate.relayProtocol) {
5087
+ return `TURN-${localCandidate.relayProtocol.toUpperCase()}`;
5088
+ }
5089
+ return (_a = localCandidate.protocol) === null || _a === void 0 ? void 0 : _a.toUpperCase();
5090
+ });
5091
+ }
4994
5092
  }
4995
5093
  PeerConnection.Events = PeerConnectionEvents;
4996
5094
 
@@ -8860,7 +8958,7 @@ function injectContentTypes(sdp, contentTypeMap) {
8860
8958
  }
8861
8959
  function injectJmpAttributes(parsedSdp, csiMap, streamSignalingMode) {
8862
8960
  parsedSdp.avMedia
8863
- .filter((mLine) => mLine.direction === 'sendrecv' || mLine.direction === 'sendonly')
8961
+ .filter((mLine) => mLine.direction === 'sendrecv' || mLine.direction === 'inactive')
8864
8962
  .forEach((mLine) => {
8865
8963
  if (!mLine.otherLines.find((line) => line instanceof JmpLine)) {
8866
8964
  mLine.addLine(new JmpLine());
@@ -8884,6 +8982,7 @@ class SendOnlyTransceiver extends Transceiver {
8884
8982
  super(rtcpRtpTransceiver);
8885
8983
  this.requested = false;
8886
8984
  this.csi = csi;
8985
+ this.direction = 'sendrecv';
8887
8986
  this.handleTrackChange = this.handleTrackChange.bind(this);
8888
8987
  }
8889
8988
  handleTrackChange() {
@@ -8901,6 +9000,7 @@ class SendOnlyTransceiver extends Transceiver {
8901
9000
  var _a;
8902
9001
  return __awaiter(this, void 0, void 0, function* () {
8903
9002
  _super.replaceTransceiver.call(this, newRtcRtpTransceiver);
9003
+ newRtcRtpTransceiver.direction = this.direction;
8904
9004
  if (this.requested) {
8905
9005
  yield this.sender.replaceTrack(((_a = this.publishedTrack) === null || _a === void 0 ? void 0 : _a.underlyingTrack) || null);
8906
9006
  }
@@ -8971,8 +9071,8 @@ class SendOnlyTransceiver extends Transceiver {
8971
9071
  return this.replacePublishedTrack();
8972
9072
  }
8973
9073
  setActive(enabled) {
8974
- const direction = enabled ? 'sendrecv' : 'inactive';
8975
- this._rtcRtpTransceiver.direction = direction;
9074
+ this.direction = enabled ? 'sendrecv' : 'inactive';
9075
+ this._rtcRtpTransceiver.direction = this.direction;
8976
9076
  return this._rtcRtpTransceiver.direction !== this._rtcRtpTransceiver.currentDirection;
8977
9077
  }
8978
9078
  getStats() {
@@ -9595,9 +9695,16 @@ const defaultMultistreamConnectionOptions = {
9595
9695
  bundlePolicy: 'max-compat',
9596
9696
  iceServers: undefined,
9597
9697
  disableContentSimulcast: true,
9698
+ enableMainAudio: true,
9699
+ enableMainVideo: true,
9700
+ };
9701
+ const defaultVideoCodecParameters = {
9702
+ 'max-mbps': `${defaultMaxVideoEncodeMbps}`,
9703
+ 'max-fs': `${defaultMaxVideoEncodeFrameSize}`,
9598
9704
  };
9599
9705
  class MultistreamConnection extends EventEmitter {
9600
9706
  constructor(userOptions = {}) {
9707
+ var _a, _b;
9601
9708
  super();
9602
9709
  this.sendTransceivers = new Map();
9603
9710
  this.recvTransceivers = new Map();
@@ -9605,6 +9712,7 @@ class MultistreamConnection extends EventEmitter {
9605
9712
  this.pendingJmpTasks = [];
9606
9713
  this.metricsCallback = () => { };
9607
9714
  this.overuseUpdateCallback = () => { };
9715
+ this.customCodecParameters = new Map();
9608
9716
  this.midMap = new Map();
9609
9717
  this.currentMid = 0;
9610
9718
  this.options = Object.assign(Object.assign({}, defaultMultistreamConnectionOptions), userOptions);
@@ -9617,11 +9725,15 @@ class MultistreamConnection extends EventEmitter {
9617
9725
  const mainSceneId = generateSceneId();
9618
9726
  const videoMainEncodingOptions = this.getVideoEncodingOptions(MediaContent.Main);
9619
9727
  this.createSendTransceiver(MediaType.VideoMain, mainSceneId, videoMainEncodingOptions);
9728
+ this.setCodecParameters(MediaType.VideoMain, defaultVideoCodecParameters);
9620
9729
  this.createSendTransceiver(MediaType.AudioMain, mainSceneId);
9730
+ (_a = this.sendTransceivers.get(MediaType.VideoMain)) === null || _a === void 0 ? void 0 : _a.setActive(this.options.enableMainVideo);
9731
+ (_b = this.sendTransceivers.get(MediaType.AudioMain)) === null || _b === void 0 ? void 0 : _b.setActive(this.options.enableMainAudio);
9621
9732
  if (this.options.floorControlledPresentation) {
9622
9733
  const videoPresentationEncodingOptions = this.getVideoEncodingOptions(MediaContent.Slides);
9623
9734
  const contentSceneId = generateSceneId();
9624
9735
  this.createSendTransceiver(MediaType.VideoSlides, contentSceneId, videoPresentationEncodingOptions);
9736
+ this.setCodecParameters(MediaType.VideoSlides, defaultVideoCodecParameters);
9625
9737
  this.createSendTransceiver(MediaType.AudioSlides, contentSceneId);
9626
9738
  }
9627
9739
  }
@@ -9986,20 +10098,7 @@ class MultistreamConnection extends EventEmitter {
9986
10098
  return ingressSignaler.getReceiverId();
9987
10099
  });
9988
10100
  if (this.pc.getRemoteDescription()) {
9989
- yield this.pc
9990
- .createOffer()
9991
- .then((offer) => {
9992
- if (!offer.sdp) {
9993
- throw new Error('No SDP offer');
9994
- }
9995
- offer.sdp = this.preProcessLocalOffer(offer.sdp);
9996
- return this.pc.setLocalDescription(offer);
9997
- })
9998
- .then(() => {
9999
- var _a;
10000
- const answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
10001
- return this.pc.setRemoteDescription({ type: 'answer', sdp: answer });
10002
- });
10101
+ yield this.doLocalOfferAnswer();
10003
10102
  }
10004
10103
  this.recvTransceivers.set(mediaType, [
10005
10104
  ...(this.recvTransceivers.get(mediaType) || []),
@@ -10065,8 +10164,26 @@ class MultistreamConnection extends EventEmitter {
10065
10164
  }
10066
10165
  setAnswer(answer) {
10067
10166
  return __awaiter(this, void 0, void 0, function* () {
10167
+ const isInitialAnswer = !this.pc.getRemoteDescription();
10068
10168
  const sdp = this.preProcessRemoteAnswer(answer);
10069
- return this.pc.setRemoteDescription({ type: 'answer', sdp });
10169
+ return this.pc.setRemoteDescription({ type: 'answer', sdp }).then(() => {
10170
+ if (isInitialAnswer && this.customCodecParameters.size > 0) {
10171
+ this.doLocalOfferAnswer();
10172
+ }
10173
+ });
10174
+ });
10175
+ }
10176
+ doLocalOfferAnswer() {
10177
+ var _a;
10178
+ return __awaiter(this, void 0, void 0, function* () {
10179
+ const offer = yield this.pc.createOffer();
10180
+ if (!offer.sdp) {
10181
+ throw new Error('No SDP offer');
10182
+ }
10183
+ offer.sdp = this.preProcessLocalOffer(offer.sdp);
10184
+ yield this.pc.setLocalDescription(offer);
10185
+ const answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
10186
+ return this.pc.setRemoteDescription({ type: 'answer', sdp: answer });
10070
10187
  });
10071
10188
  }
10072
10189
  enableMultistreamAudio(enabled) {
@@ -10093,12 +10210,20 @@ class MultistreamConnection extends EventEmitter {
10093
10210
  const simulcastEnabled = !!av.simulcast;
10094
10211
  const rtxEnabled = av.type === 'video';
10095
10212
  egressSignaler.signalStreams(simulcastEnabled, rtxEnabled, av);
10096
- if (av.type === 'video') {
10213
+ 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; });
10214
+ if (mediaType && this.customCodecParameters.has(mediaType)) {
10097
10215
  [...av.codecs.values()]
10098
- .filter((ci) => ci.name === 'H264')
10216
+ .filter((ci) => ci.name === (av.type === 'audio' ? 'opus' : 'H264'))
10099
10217
  .forEach((ci) => {
10100
- ci.fmtParams.set('max-mbps', `${defaultMaxVideoEncodeMbps}`);
10101
- ci.fmtParams.set('max-fs', `${defaultMaxVideoEncodeFrameSize}`);
10218
+ var _a;
10219
+ (_a = this.customCodecParameters.get(mediaType)) === null || _a === void 0 ? void 0 : _a.forEach((value, param) => {
10220
+ if (value === null) {
10221
+ ci.fmtParams.delete(param);
10222
+ }
10223
+ else {
10224
+ ci.fmtParams.set(param, `${value}`);
10225
+ }
10226
+ });
10102
10227
  });
10103
10228
  }
10104
10229
  });
@@ -10154,6 +10279,30 @@ class MultistreamConnection extends EventEmitter {
10154
10279
  .map((transceiver) => transceiver.publishedTrack)
10155
10280
  .filter(Boolean);
10156
10281
  }
10282
+ setCodecParameters(mediaType, parameters) {
10283
+ return __awaiter(this, void 0, void 0, function* () {
10284
+ const currentParams = this.customCodecParameters.get(mediaType) || new Map();
10285
+ Object.entries(parameters).forEach(([param, value]) => {
10286
+ currentParams.set(param, value);
10287
+ });
10288
+ this.customCodecParameters.set(mediaType, currentParams);
10289
+ if (this.pc.getRemoteDescription()) {
10290
+ yield this.doLocalOfferAnswer();
10291
+ }
10292
+ });
10293
+ }
10294
+ deleteCodecParameters(mediaType, parameters) {
10295
+ return __awaiter(this, void 0, void 0, function* () {
10296
+ const currentParams = this.customCodecParameters.get(mediaType) || new Map();
10297
+ parameters.forEach((param) => {
10298
+ currentParams.set(param, null);
10299
+ });
10300
+ this.customCodecParameters.set(mediaType, currentParams);
10301
+ if (this.pc.getRemoteDescription()) {
10302
+ yield this.doLocalOfferAnswer();
10303
+ }
10304
+ });
10305
+ }
10157
10306
  requestMedia(mediaType, mediaRequests) {
10158
10307
  var _a;
10159
10308
  const task = () => {