@webex/web-client-media-engine 1.39.0 → 1.40.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 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
 
@@ -8881,6 +8979,9 @@ function injectJmpAttributes(parsedSdp, csiMap, streamSignalingMode) {
8881
8979
  mLine.addLine(new JmpStreamIdModeLine(streamSignalingMode));
8882
8980
  }
8883
8981
  });
8982
+ }
8983
+ function hasSimulcast(av) {
8984
+ return !!av.simulcast || av.ssrcGroups.map((sg) => sg.semantics).some((sem) => sem === 'SIM');
8884
8985
  }
8885
8986
 
8886
8987
  class SendOnlyTransceiver extends Transceiver {
@@ -9604,6 +9705,10 @@ const defaultMultistreamConnectionOptions = {
9604
9705
  enableMainAudio: true,
9605
9706
  enableMainVideo: true,
9606
9707
  };
9708
+ const defaultVideoCodecParameters = {
9709
+ 'max-mbps': `${defaultMaxVideoEncodeMbps}`,
9710
+ 'max-fs': `${defaultMaxVideoEncodeFrameSize}`,
9711
+ };
9607
9712
  class MultistreamConnection extends EventEmitter {
9608
9713
  constructor(userOptions = {}) {
9609
9714
  var _a, _b;
@@ -9614,6 +9719,7 @@ class MultistreamConnection extends EventEmitter {
9614
9719
  this.pendingJmpTasks = [];
9615
9720
  this.metricsCallback = () => { };
9616
9721
  this.overuseUpdateCallback = () => { };
9722
+ this.customCodecParameters = new Map();
9617
9723
  this.midMap = new Map();
9618
9724
  this.currentMid = 0;
9619
9725
  this.options = Object.assign(Object.assign({}, defaultMultistreamConnectionOptions), userOptions);
@@ -9626,6 +9732,7 @@ class MultistreamConnection extends EventEmitter {
9626
9732
  const mainSceneId = generateSceneId();
9627
9733
  const videoMainEncodingOptions = this.getVideoEncodingOptions(MediaContent.Main);
9628
9734
  this.createSendTransceiver(MediaType.VideoMain, mainSceneId, videoMainEncodingOptions);
9735
+ this.setCodecParameters(MediaType.VideoMain, defaultVideoCodecParameters);
9629
9736
  this.createSendTransceiver(MediaType.AudioMain, mainSceneId);
9630
9737
  (_a = this.sendTransceivers.get(MediaType.VideoMain)) === null || _a === void 0 ? void 0 : _a.setActive(this.options.enableMainVideo);
9631
9738
  (_b = this.sendTransceivers.get(MediaType.AudioMain)) === null || _b === void 0 ? void 0 : _b.setActive(this.options.enableMainAudio);
@@ -9633,6 +9740,7 @@ class MultistreamConnection extends EventEmitter {
9633
9740
  const videoPresentationEncodingOptions = this.getVideoEncodingOptions(MediaContent.Slides);
9634
9741
  const contentSceneId = generateSceneId();
9635
9742
  this.createSendTransceiver(MediaType.VideoSlides, contentSceneId, videoPresentationEncodingOptions);
9743
+ this.setCodecParameters(MediaType.VideoSlides, defaultVideoCodecParameters);
9636
9744
  this.createSendTransceiver(MediaType.AudioSlides, contentSceneId);
9637
9745
  }
9638
9746
  }
@@ -9997,20 +10105,7 @@ class MultistreamConnection extends EventEmitter {
9997
10105
  return ingressSignaler.getReceiverId();
9998
10106
  });
9999
10107
  if (this.pc.getRemoteDescription()) {
10000
- yield this.pc
10001
- .createOffer()
10002
- .then((offer) => {
10003
- if (!offer.sdp) {
10004
- throw new Error('No SDP offer');
10005
- }
10006
- offer.sdp = this.preProcessLocalOffer(offer.sdp);
10007
- return this.pc.setLocalDescription(offer);
10008
- })
10009
- .then(() => {
10010
- var _a;
10011
- const answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
10012
- return this.pc.setRemoteDescription({ type: 'answer', sdp: answer });
10013
- });
10108
+ yield this.doLocalOfferAnswer();
10014
10109
  }
10015
10110
  this.recvTransceivers.set(mediaType, [
10016
10111
  ...(this.recvTransceivers.get(mediaType) || []),
@@ -10076,8 +10171,26 @@ class MultistreamConnection extends EventEmitter {
10076
10171
  }
10077
10172
  setAnswer(answer) {
10078
10173
  return __awaiter(this, void 0, void 0, function* () {
10174
+ const isInitialAnswer = !this.pc.getRemoteDescription();
10079
10175
  const sdp = this.preProcessRemoteAnswer(answer);
10080
- return this.pc.setRemoteDescription({ type: 'answer', sdp });
10176
+ return this.pc.setRemoteDescription({ type: 'answer', sdp }).then(() => {
10177
+ if (isInitialAnswer && this.customCodecParameters.size > 0) {
10178
+ this.doLocalOfferAnswer();
10179
+ }
10180
+ });
10181
+ });
10182
+ }
10183
+ doLocalOfferAnswer() {
10184
+ var _a;
10185
+ return __awaiter(this, void 0, void 0, function* () {
10186
+ const offer = yield this.pc.createOffer();
10187
+ if (!offer.sdp) {
10188
+ throw new Error('No SDP offer');
10189
+ }
10190
+ offer.sdp = this.preProcessLocalOffer(offer.sdp);
10191
+ yield this.pc.setLocalDescription(offer);
10192
+ const answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
10193
+ return this.pc.setRemoteDescription({ type: 'answer', sdp: answer });
10081
10194
  });
10082
10195
  }
10083
10196
  enableMultistreamAudio(enabled) {
@@ -10101,15 +10214,23 @@ class MultistreamConnection extends EventEmitter {
10101
10214
  .filter((av) => av.direction === 'sendrecv')
10102
10215
  .forEach((av) => {
10103
10216
  const egressSignaler = this.streamSignalerManager.getOrCreateEgressStreamSignaler(av.mid);
10104
- const simulcastEnabled = !!av.simulcast;
10217
+ const simulcastEnabled = hasSimulcast(av);
10105
10218
  const rtxEnabled = av.type === 'video';
10106
10219
  egressSignaler.signalStreams(simulcastEnabled, rtxEnabled, av);
10107
- if (av.type === 'video') {
10220
+ 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; });
10221
+ if (mediaType && this.customCodecParameters.has(mediaType)) {
10108
10222
  [...av.codecs.values()]
10109
- .filter((ci) => ci.name === 'H264')
10223
+ .filter((ci) => ci.name === (av.type === 'audio' ? 'opus' : 'H264'))
10110
10224
  .forEach((ci) => {
10111
- ci.fmtParams.set('max-mbps', `${defaultMaxVideoEncodeMbps}`);
10112
- ci.fmtParams.set('max-fs', `${defaultMaxVideoEncodeFrameSize}`);
10225
+ var _a;
10226
+ (_a = this.customCodecParameters.get(mediaType)) === null || _a === void 0 ? void 0 : _a.forEach((value, param) => {
10227
+ if (value === null) {
10228
+ ci.fmtParams.delete(param);
10229
+ }
10230
+ else {
10231
+ ci.fmtParams.set(param, `${value}`);
10232
+ }
10233
+ });
10113
10234
  });
10114
10235
  }
10115
10236
  });
@@ -10165,6 +10286,30 @@ class MultistreamConnection extends EventEmitter {
10165
10286
  .map((transceiver) => transceiver.publishedTrack)
10166
10287
  .filter(Boolean);
10167
10288
  }
10289
+ setCodecParameters(mediaType, parameters) {
10290
+ return __awaiter(this, void 0, void 0, function* () {
10291
+ const currentParams = this.customCodecParameters.get(mediaType) || new Map();
10292
+ Object.entries(parameters).forEach(([param, value]) => {
10293
+ currentParams.set(param, value);
10294
+ });
10295
+ this.customCodecParameters.set(mediaType, currentParams);
10296
+ if (this.pc.getRemoteDescription()) {
10297
+ yield this.doLocalOfferAnswer();
10298
+ }
10299
+ });
10300
+ }
10301
+ deleteCodecParameters(mediaType, parameters) {
10302
+ return __awaiter(this, void 0, void 0, function* () {
10303
+ const currentParams = this.customCodecParameters.get(mediaType) || new Map();
10304
+ parameters.forEach((param) => {
10305
+ currentParams.set(param, null);
10306
+ });
10307
+ this.customCodecParameters.set(mediaType, currentParams);
10308
+ if (this.pc.getRemoteDescription()) {
10309
+ yield this.doLocalOfferAnswer();
10310
+ }
10311
+ });
10312
+ }
10168
10313
  requestMedia(mediaType, mediaRequests) {
10169
10314
  var _a;
10170
10315
  const task = () => {