@webex/web-client-media-engine 1.39.0 → 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
 
@@ -9600,6 +9698,10 @@ const defaultMultistreamConnectionOptions = {
9600
9698
  enableMainAudio: true,
9601
9699
  enableMainVideo: true,
9602
9700
  };
9701
+ const defaultVideoCodecParameters = {
9702
+ 'max-mbps': `${defaultMaxVideoEncodeMbps}`,
9703
+ 'max-fs': `${defaultMaxVideoEncodeFrameSize}`,
9704
+ };
9603
9705
  class MultistreamConnection extends EventEmitter {
9604
9706
  constructor(userOptions = {}) {
9605
9707
  var _a, _b;
@@ -9610,6 +9712,7 @@ class MultistreamConnection extends EventEmitter {
9610
9712
  this.pendingJmpTasks = [];
9611
9713
  this.metricsCallback = () => { };
9612
9714
  this.overuseUpdateCallback = () => { };
9715
+ this.customCodecParameters = new Map();
9613
9716
  this.midMap = new Map();
9614
9717
  this.currentMid = 0;
9615
9718
  this.options = Object.assign(Object.assign({}, defaultMultistreamConnectionOptions), userOptions);
@@ -9622,6 +9725,7 @@ class MultistreamConnection extends EventEmitter {
9622
9725
  const mainSceneId = generateSceneId();
9623
9726
  const videoMainEncodingOptions = this.getVideoEncodingOptions(MediaContent.Main);
9624
9727
  this.createSendTransceiver(MediaType.VideoMain, mainSceneId, videoMainEncodingOptions);
9728
+ this.setCodecParameters(MediaType.VideoMain, defaultVideoCodecParameters);
9625
9729
  this.createSendTransceiver(MediaType.AudioMain, mainSceneId);
9626
9730
  (_a = this.sendTransceivers.get(MediaType.VideoMain)) === null || _a === void 0 ? void 0 : _a.setActive(this.options.enableMainVideo);
9627
9731
  (_b = this.sendTransceivers.get(MediaType.AudioMain)) === null || _b === void 0 ? void 0 : _b.setActive(this.options.enableMainAudio);
@@ -9629,6 +9733,7 @@ class MultistreamConnection extends EventEmitter {
9629
9733
  const videoPresentationEncodingOptions = this.getVideoEncodingOptions(MediaContent.Slides);
9630
9734
  const contentSceneId = generateSceneId();
9631
9735
  this.createSendTransceiver(MediaType.VideoSlides, contentSceneId, videoPresentationEncodingOptions);
9736
+ this.setCodecParameters(MediaType.VideoSlides, defaultVideoCodecParameters);
9632
9737
  this.createSendTransceiver(MediaType.AudioSlides, contentSceneId);
9633
9738
  }
9634
9739
  }
@@ -9993,20 +10098,7 @@ class MultistreamConnection extends EventEmitter {
9993
10098
  return ingressSignaler.getReceiverId();
9994
10099
  });
9995
10100
  if (this.pc.getRemoteDescription()) {
9996
- yield this.pc
9997
- .createOffer()
9998
- .then((offer) => {
9999
- if (!offer.sdp) {
10000
- throw new Error('No SDP offer');
10001
- }
10002
- offer.sdp = this.preProcessLocalOffer(offer.sdp);
10003
- return this.pc.setLocalDescription(offer);
10004
- })
10005
- .then(() => {
10006
- var _a;
10007
- const answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
10008
- return this.pc.setRemoteDescription({ type: 'answer', sdp: answer });
10009
- });
10101
+ yield this.doLocalOfferAnswer();
10010
10102
  }
10011
10103
  this.recvTransceivers.set(mediaType, [
10012
10104
  ...(this.recvTransceivers.get(mediaType) || []),
@@ -10072,8 +10164,26 @@ class MultistreamConnection extends EventEmitter {
10072
10164
  }
10073
10165
  setAnswer(answer) {
10074
10166
  return __awaiter(this, void 0, void 0, function* () {
10167
+ const isInitialAnswer = !this.pc.getRemoteDescription();
10075
10168
  const sdp = this.preProcessRemoteAnswer(answer);
10076
- 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 });
10077
10187
  });
10078
10188
  }
10079
10189
  enableMultistreamAudio(enabled) {
@@ -10100,12 +10210,20 @@ class MultistreamConnection extends EventEmitter {
10100
10210
  const simulcastEnabled = !!av.simulcast;
10101
10211
  const rtxEnabled = av.type === 'video';
10102
10212
  egressSignaler.signalStreams(simulcastEnabled, rtxEnabled, av);
10103
- 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)) {
10104
10215
  [...av.codecs.values()]
10105
- .filter((ci) => ci.name === 'H264')
10216
+ .filter((ci) => ci.name === (av.type === 'audio' ? 'opus' : 'H264'))
10106
10217
  .forEach((ci) => {
10107
- ci.fmtParams.set('max-mbps', `${defaultMaxVideoEncodeMbps}`);
10108
- 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
+ });
10109
10227
  });
10110
10228
  }
10111
10229
  });
@@ -10161,6 +10279,30 @@ class MultistreamConnection extends EventEmitter {
10161
10279
  .map((transceiver) => transceiver.publishedTrack)
10162
10280
  .filter(Boolean);
10163
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
+ }
10164
10306
  requestMedia(mediaType, mediaRequests) {
10165
10307
  var _a;
10166
10308
  const task = () => {