@webex/internal-media-core 1.35.1 → 1.35.3

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
@@ -4926,6 +4926,10 @@ exports.LocalTrackEvents = void 0;
4926
4926
  * Fires when there has been a change in the underlying track.
4927
4927
  */
4928
4928
  LocalTrackEvents["UnderlyingTrackChange"] = "underlying-track-change";
4929
+ /**
4930
+ * Fires when the applyConstraints() has been called for the track.
4931
+ */
4932
+ LocalTrackEvents["TrackConstraintsChange"] = "track-constraints-change";
4929
4933
  })(exports.LocalTrackEvents || (exports.LocalTrackEvents = {}));
4930
4934
  // TBD: Fix this once types are published separately
4931
4935
  // export type TrackEffect = BaseMicrophoneEffect | BaseCameraEffect;
@@ -5114,7 +5118,7 @@ class LocalTrack extends EventEmitter$2 {
5114
5118
  return effect;
5115
5119
  }
5116
5120
  /**
5117
- * Cleanup local microphone track.
5121
+ * Cleanup the local microphone track.
5118
5122
  */
5119
5123
  disposeEffects() {
5120
5124
  if (this.effects.size > 0) {
@@ -5124,6 +5128,57 @@ class LocalTrack extends EventEmitter$2 {
5124
5128
  this.emit(exports.LocalTrackEvents.UnderlyingTrackChange);
5125
5129
  }
5126
5130
  }
5131
+ /**
5132
+ * Apply constraints to the track.
5133
+ *
5134
+ * @param constraints - The constraints to apply to the track.
5135
+ * @returns A promise which resolves when the constraints have been successfully applied.
5136
+ */
5137
+ applyConstraints(constraints) {
5138
+ return __awaiter$1(this, void 0, void 0, function* () {
5139
+ logger$3.log("Applying constraints to local track:", constraints);
5140
+ var ret = this.underlyingTrack.applyConstraints(constraints).then(() => {
5141
+ this.emit(exports.LocalTrackEvents.TrackConstraintsChange);
5142
+ });
5143
+ return ret;
5144
+ });
5145
+ }
5146
+ /**
5147
+ * Get the current constraints of the track.
5148
+ *
5149
+ * @returns The constraints of the track.
5150
+ */
5151
+ getConstraints() {
5152
+ return this.underlyingTrack.getConstraints();
5153
+ }
5154
+ /**
5155
+ * Get the current settings of the track.
5156
+ *
5157
+ * @returns The settings of the track.
5158
+ */
5159
+ getSettings() {
5160
+ return this.underlyingTrack.getSettings();
5161
+ }
5162
+ /**
5163
+ * Check the resolution and then return how many layers will be active.
5164
+ *
5165
+ * @returns The active layers count.
5166
+ */
5167
+ getNumActiveSimulcastLayers() {
5168
+ var activeSimulcastLayersNumber = 0;
5169
+ if (this.trackState.type === 'audio') {
5170
+ return activeSimulcastLayersNumber;
5171
+ }
5172
+ var videoHeight = this.underlyingTrack.getSettings().height;
5173
+ if (videoHeight <= 180) {
5174
+ activeSimulcastLayersNumber = 1;
5175
+ } else if (videoHeight <= 360) {
5176
+ activeSimulcastLayersNumber = 2;
5177
+ } else {
5178
+ activeSimulcastLayersNumber = 3;
5179
+ }
5180
+ return activeSimulcastLayersNumber;
5181
+ }
5127
5182
  }
5128
5183
  LocalTrack.Events = exports.LocalTrackEvents;
5129
5184
 
@@ -8552,6 +8607,44 @@ class PeerConnection extends EventEmitter$2 {
8552
8607
  get iceGatheringState() {
8553
8608
  return this.pc.iceGatheringState;
8554
8609
  }
8610
+ /**
8611
+ * Returns the type of a connection that has been established.
8612
+ *
8613
+ * @returns The connection type which would be `ConnectionType`.
8614
+ */
8615
+ getCurrentConnectionType() {
8616
+ var _a;
8617
+ return __awaiter$1(this, void 0, void 0, function* () {
8618
+ // make sure this method only can be called when the ice connection is established;
8619
+ var isIceConnected = this.pc.iceConnectionState === 'connected' || this.pc.iceConnectionState === 'completed';
8620
+ if (!isIceConnected) {
8621
+ throw new Error('Ice connection is not established');
8622
+ }
8623
+ var succeededLocalCandidateIds = new Set();
8624
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8625
+ var localCandidateStatsReports = [];
8626
+ (yield this.pc.getStats()).forEach(report => {
8627
+ var _a;
8628
+ // collect all local candidate ids from `candidate-pair` stats reports with `succeeded` state.
8629
+ if (report.type === 'candidate-pair' && ((_a = report.state) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'succeeded') {
8630
+ succeededLocalCandidateIds.add(report.localCandidateId);
8631
+ }
8632
+ // collect all `local-candidate` stats.
8633
+ if (report.type === 'local-candidate') {
8634
+ localCandidateStatsReports.push(report);
8635
+ }
8636
+ });
8637
+ // find the `local-candidate` stats which report id contains in `succeededLocalCandidateIds`.
8638
+ var localCandidate = localCandidateStatsReports.find(report => succeededLocalCandidateIds.has(report.id));
8639
+ if (!localCandidate) {
8640
+ return 'unknown';
8641
+ }
8642
+ if (localCandidate.relayProtocol) {
8643
+ return "TURN-".concat(localCandidate.relayProtocol.toUpperCase());
8644
+ }
8645
+ return (_a = localCandidate.protocol) === null || _a === void 0 ? void 0 : _a.toUpperCase();
8646
+ });
8647
+ }
8555
8648
  }
8556
8649
  PeerConnection.Events = PeerConnectionEvents;
8557
8650
 
@@ -12132,7 +12225,7 @@ function injectContentTypes(sdp, contentTypeMap) {
12132
12225
  });
12133
12226
  }
12134
12227
  function injectJmpAttributes(parsedSdp, csiMap, streamSignalingMode) {
12135
- parsedSdp.avMedia.filter(mLine => mLine.direction === 'sendrecv' || mLine.direction === 'sendonly').forEach(mLine => {
12228
+ parsedSdp.avMedia.filter(mLine => mLine.direction === 'sendrecv' || mLine.direction === 'inactive').forEach(mLine => {
12136
12229
  if (!mLine.otherLines.find(line => line instanceof JmpLine)) {
12137
12230
  mLine.addLine(new JmpLine());
12138
12231
  }
@@ -12149,11 +12242,15 @@ function injectJmpAttributes(parsedSdp, csiMap, streamSignalingMode) {
12149
12242
  }
12150
12243
  });
12151
12244
  }
12245
+ function hasSimulcast(av) {
12246
+ return !!av.simulcast || av.ssrcGroups.map(sg => sg.semantics).some(sem => sem === 'SIM');
12247
+ }
12152
12248
  class SendOnlyTransceiver extends Transceiver {
12153
12249
  constructor(rtcpRtpTransceiver, csi) {
12154
12250
  super(rtcpRtpTransceiver);
12155
12251
  this.requested = false;
12156
12252
  this.csi = csi;
12253
+ this.direction = 'sendrecv';
12157
12254
  this.handleTrackChange = this.handleTrackChange.bind(this);
12158
12255
  }
12159
12256
  handleTrackChange() {
@@ -12173,6 +12270,7 @@ class SendOnlyTransceiver extends Transceiver {
12173
12270
  var _a;
12174
12271
  return __awaiter(this, void 0, void 0, function* () {
12175
12272
  _super.replaceTransceiver.call(this, newRtcRtpTransceiver);
12273
+ newRtcRtpTransceiver.direction = this.direction;
12176
12274
  if (this.requested) {
12177
12275
  yield this.sender.replaceTrack(((_a = this.publishedTrack) === null || _a === void 0 ? void 0 : _a.underlyingTrack) || null);
12178
12276
  }
@@ -12242,8 +12340,8 @@ class SendOnlyTransceiver extends Transceiver {
12242
12340
  return this.replacePublishedTrack();
12243
12341
  }
12244
12342
  setActive(enabled) {
12245
- var direction = enabled ? 'sendrecv' : 'inactive';
12246
- this._rtcRtpTransceiver.direction = direction;
12343
+ this.direction = enabled ? 'sendrecv' : 'inactive';
12344
+ this._rtcRtpTransceiver.direction = this.direction;
12247
12345
  return this._rtcRtpTransceiver.direction !== this._rtcRtpTransceiver.currentDirection;
12248
12346
  }
12249
12347
  getStats() {
@@ -14141,11 +14239,15 @@ var defaultMultistreamConnectionOptions = {
14141
14239
  disableSimulcast: getBrowserDetails().name === 'Firefox',
14142
14240
  streamSignalingMode: 'SSRC',
14143
14241
  bundlePolicy: 'max-compat',
14144
- iceServers: undefined
14242
+ iceServers: undefined,
14243
+ disableContentSimulcast: true,
14244
+ enableMainAudio: true,
14245
+ enableMainVideo: true
14145
14246
  };
14146
14247
  class MultistreamConnection extends EventEmitter {
14147
14248
  constructor() {
14148
14249
  var userOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
14250
+ var _a, _b;
14149
14251
  super();
14150
14252
  this.sendTransceivers = new Map();
14151
14253
  this.recvTransceivers = new Map();
@@ -14153,6 +14255,7 @@ class MultistreamConnection extends EventEmitter {
14153
14255
  this.pendingJmpTasks = [];
14154
14256
  this.metricsCallback = () => {};
14155
14257
  this.overuseUpdateCallback = () => {};
14258
+ this.customCodecParameters = new Map();
14156
14259
  this.midMap = new Map();
14157
14260
  this.currentMid = 0;
14158
14261
  this.options = Object.assign(Object.assign({}, defaultMultistreamConnectionOptions), userOptions);
@@ -14163,11 +14266,13 @@ class MultistreamConnection extends EventEmitter {
14163
14266
  this.overuseStateManager.start();
14164
14267
  this.statsManager = new StatsManager(() => this.pc.getStats(), stats => this.preProcessStats(stats));
14165
14268
  var mainSceneId = generateSceneId();
14166
- var videoMainEncodingOptions = this.getVideoEncodingOptions();
14269
+ var videoMainEncodingOptions = this.getVideoEncodingOptions(MediaContent$1.Main);
14167
14270
  this.createSendTransceiver(MediaType.VideoMain, mainSceneId, videoMainEncodingOptions);
14168
14271
  this.createSendTransceiver(MediaType.AudioMain, mainSceneId);
14272
+ (_a = this.sendTransceivers.get(MediaType.VideoMain)) === null || _a === void 0 ? void 0 : _a.setActive(this.options.enableMainVideo);
14273
+ (_b = this.sendTransceivers.get(MediaType.AudioMain)) === null || _b === void 0 ? void 0 : _b.setActive(this.options.enableMainAudio);
14169
14274
  if (this.options.floorControlledPresentation) {
14170
- var videoPresentationEncodingOptions = this.getVideoEncodingOptions();
14275
+ var videoPresentationEncodingOptions = this.getVideoEncodingOptions(MediaContent$1.Slides);
14171
14276
  var contentSceneId = generateSceneId();
14172
14277
  this.createSendTransceiver(MediaType.VideoSlides, contentSceneId, videoPresentationEncodingOptions);
14173
14278
  this.createSendTransceiver(MediaType.AudioSlides, contentSceneId);
@@ -14197,8 +14302,9 @@ class MultistreamConnection extends EventEmitter {
14197
14302
  getConnectionState() {
14198
14303
  return this.pc.getConnectionState();
14199
14304
  }
14200
- getVideoEncodingOptions() {
14201
- return !this.options.disableSimulcast ? [{
14305
+ getVideoEncodingOptions(content) {
14306
+ var enabledSimulcast = content === MediaContent$1.Main ? !this.options.disableSimulcast : !this.options.disableContentSimulcast;
14307
+ return enabledSimulcast ? [{
14202
14308
  scaleResolutionDownBy: 4,
14203
14309
  active: false
14204
14310
  }, {
@@ -14286,9 +14392,11 @@ class MultistreamConnection extends EventEmitter {
14286
14392
  this.jmpSessions.set(mediaType, jmpSession);
14287
14393
  }
14288
14394
  sendSourceWarnings(mediaType, requests) {
14395
+ var _a;
14289
14396
  if (getMediaFamily$1(mediaType) === MediaFamily.Video) {
14290
14397
  var sendTransceiver = this.getSendTransceiverOrThrow(mediaType);
14291
14398
  var signaler = this.streamSignalerManager.getEgressStreamSignalerOrThrow(sendTransceiver.mid);
14399
+ var activeSimulcastLayerNumber = ((_a = sendTransceiver.publishedTrack) === null || _a === void 0 ? void 0 : _a.getNumActiveSimulcastLayers()) || 0;
14292
14400
  var sourceWarnings = [];
14293
14401
  requests.forEach(_ref5 => {
14294
14402
  var {
@@ -14308,6 +14416,12 @@ class MultistreamConnection extends EventEmitter {
14308
14416
  state: 'invalid source',
14309
14417
  csi: sendTransceiver.csi
14310
14418
  });
14419
+ } else if (signaler.getEncodingIndexForStreamId(id) > activeSimulcastLayerNumber) {
14420
+ sourceWarnings.push({
14421
+ id,
14422
+ state: 'no source',
14423
+ csi: sendTransceiver.csi
14424
+ });
14311
14425
  }
14312
14426
  });
14313
14427
  });
@@ -14484,22 +14598,19 @@ class MultistreamConnection extends EventEmitter {
14484
14598
  });
14485
14599
  }
14486
14600
  addTrackListeners(mediaType, track) {
14487
- var onTrackMute = event => {
14488
- var sendTransceiver = this.getSendTransceiverOrThrow(mediaType);
14489
- var signaler = this.streamSignalerManager.getEgressStreamSignaler(sendTransceiver.mid);
14490
- if (!signaler) {
14491
- return;
14601
+ var onTrackResolutionChange = () => {
14602
+ var sources = this.getVideoSources(mediaType);
14603
+ if (sources != null) {
14604
+ this.sendSourceIndication(mediaType, 1, sources);
14492
14605
  }
14493
- if (this.getPublishedTracks().includes(track)) {
14494
- if (getMediaFamily$1(mediaType) === MediaFamily.Audio) {
14495
- this.sendSourceIndication(mediaType, +!event.trackState.muted);
14496
- } else {
14497
- var state = event.trackState.muted ? 'avatar' : 'live';
14498
- var sources = signaler.getSenderIds().map(id => ({
14499
- id,
14500
- state,
14501
- csi: sendTransceiver.csi
14502
- }));
14606
+ };
14607
+ track.on(LocalTrack.Events.TrackConstraintsChange, onTrackResolutionChange);
14608
+ var onTrackMute = event => {
14609
+ if (getMediaFamily$1(mediaType) === MediaFamily.Audio) {
14610
+ this.sendSourceIndication(mediaType, +!event.trackState.muted);
14611
+ } else {
14612
+ var sources = this.getVideoSources(mediaType);
14613
+ if (sources != null) {
14503
14614
  this.sendSourceIndication(mediaType, +!event.trackState.muted, sources);
14504
14615
  }
14505
14616
  }
@@ -14509,28 +14620,47 @@ class MultistreamConnection extends EventEmitter {
14509
14620
  if (!event.isPublished) {
14510
14621
  track.off(LocalTrack.Events.Muted, onTrackMute);
14511
14622
  track.off(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);
14623
+ track.off(LocalTrack.Events.TrackConstraintsChange, onTrackResolutionChange);
14512
14624
  }
14513
- var sendTransceiver = this.getSendTransceiverOrThrow(mediaType);
14514
- var signaler = this.streamSignalerManager.getEgressStreamSignaler(sendTransceiver.mid);
14515
- if (!signaler) {
14516
- return;
14517
- }
14518
- if (!event.trackState.muted) {
14519
- if (getMediaFamily$1(mediaType) === MediaFamily.Audio) {
14520
- this.sendSourceIndication(mediaType, +event.isPublished);
14521
- } else {
14522
- var state = event.isPublished ? 'live' : 'no source';
14523
- var sources = signaler.getSenderIds().map(id => ({
14524
- id,
14525
- state,
14526
- csi: sendTransceiver.csi
14527
- }));
14625
+ if (getMediaFamily$1(mediaType) === MediaFamily.Audio) {
14626
+ this.sendSourceIndication(mediaType, +event.isPublished);
14627
+ } else {
14628
+ var sources = this.getVideoSources(mediaType);
14629
+ if (sources != null) {
14528
14630
  this.sendSourceIndication(mediaType, +event.isPublished, sources);
14529
14631
  }
14530
14632
  }
14531
14633
  };
14532
14634
  track.on(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);
14533
14635
  }
14636
+ getVideoSources(mediaType) {
14637
+ var _a, _b, _c;
14638
+ var sendTransceiver = this.getSendTransceiverOrThrow(mediaType);
14639
+ var signaler = this.streamSignalerManager.getEgressStreamSignaler(sendTransceiver.mid);
14640
+ if (!signaler) {
14641
+ return null;
14642
+ }
14643
+ var activeSimulcastLayerNumber = ((_a = sendTransceiver.publishedTrack) === null || _a === void 0 ? void 0 : _a.getNumActiveSimulcastLayers()) || 0;
14644
+ var published = (_b = sendTransceiver.publishedTrack) === null || _b === void 0 ? void 0 : _b.published;
14645
+ var muted = ((_c = sendTransceiver.publishedTrack) === null || _c === void 0 ? void 0 : _c.muted) === true;
14646
+ return signaler.getSenderIds().map(id => {
14647
+ var state;
14648
+ if (!published) {
14649
+ state = 'no source';
14650
+ } else if (muted) {
14651
+ state = 'avatar';
14652
+ } else if (activeSimulcastLayerNumber <= signaler.getEncodingIndexForStreamId(id)) {
14653
+ state = 'no source';
14654
+ } else {
14655
+ state = 'live';
14656
+ }
14657
+ return {
14658
+ id,
14659
+ state,
14660
+ csi: sendTransceiver.csi
14661
+ };
14662
+ });
14663
+ }
14534
14664
  createReceiveSlot(mediaType) {
14535
14665
  return __awaiter(this, void 0, void 0, function* () {
14536
14666
  var rtcRtpTransceiver = this.pc.addTransceiver(toMediaStreamTrackKind(mediaType), {
@@ -14545,20 +14675,7 @@ class MultistreamConnection extends EventEmitter {
14545
14675
  return ingressSignaler.getReceiverId();
14546
14676
  });
14547
14677
  if (this.pc.getRemoteDescription()) {
14548
- yield this.pc.createOffer().then(offer => {
14549
- if (!offer.sdp) {
14550
- throw new Error('No SDP offer');
14551
- }
14552
- offer.sdp = this.preProcessLocalOffer(offer.sdp);
14553
- return this.pc.setLocalDescription(offer);
14554
- }).then(() => {
14555
- var _a;
14556
- var answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
14557
- return this.pc.setRemoteDescription({
14558
- type: 'answer',
14559
- sdp: answer
14560
- });
14561
- });
14678
+ yield this.doLocalOfferAnswer();
14562
14679
  }
14563
14680
  this.recvTransceivers.set(mediaType, [...(this.recvTransceivers.get(mediaType) || []), recvOnlyTransceiver]);
14564
14681
  return recvOnlyTransceiver.receiveSlot;
@@ -14606,10 +14723,31 @@ class MultistreamConnection extends EventEmitter {
14606
14723
  }
14607
14724
  setAnswer(answer) {
14608
14725
  return __awaiter(this, void 0, void 0, function* () {
14726
+ var isInitialAnswer = !this.pc.getRemoteDescription();
14609
14727
  var sdp = this.preProcessRemoteAnswer(answer);
14610
14728
  return this.pc.setRemoteDescription({
14611
14729
  type: 'answer',
14612
14730
  sdp
14731
+ }).then(() => {
14732
+ if (isInitialAnswer && this.customCodecParameters.size > 0) {
14733
+ this.doLocalOfferAnswer();
14734
+ }
14735
+ });
14736
+ });
14737
+ }
14738
+ doLocalOfferAnswer() {
14739
+ var _a;
14740
+ return __awaiter(this, void 0, void 0, function* () {
14741
+ var offer = yield this.pc.createOffer();
14742
+ if (!offer.sdp) {
14743
+ throw new Error('No SDP offer');
14744
+ }
14745
+ offer.sdp = this.preProcessLocalOffer(offer.sdp);
14746
+ yield this.pc.setLocalDescription(offer);
14747
+ var answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
14748
+ return this.pc.setRemoteDescription({
14749
+ type: 'answer',
14750
+ sdp: answer
14613
14751
  });
14614
14752
  });
14615
14753
  }
@@ -14630,7 +14768,7 @@ class MultistreamConnection extends EventEmitter {
14630
14768
  });
14631
14769
  parsed.avMedia.filter(av => av.direction === 'sendrecv').forEach(av => {
14632
14770
  var egressSignaler = this.streamSignalerManager.getOrCreateEgressStreamSignaler(av.mid);
14633
- var simulcastEnabled = av.type === 'video' ? !this.options.disableSimulcast : false;
14771
+ var simulcastEnabled = hasSimulcast(av);
14634
14772
  var rtxEnabled = av.type === 'video';
14635
14773
  egressSignaler.signalStreams(simulcastEnabled, rtxEnabled, av);
14636
14774
  if (av.type === 'video') {
@@ -14639,6 +14777,22 @@ class MultistreamConnection extends EventEmitter {
14639
14777
  ci.fmtParams.set('max-fs', "".concat(defaultMaxVideoEncodeFrameSize));
14640
14778
  });
14641
14779
  }
14780
+ var mediaType = [...this.sendTransceivers.keys()].find(key => {
14781
+ var _a;
14782
+ return ((_a = this.sendTransceivers.get(key)) === null || _a === void 0 ? void 0 : _a.mid) === av.mid;
14783
+ });
14784
+ if (mediaType && this.customCodecParameters.has(mediaType)) {
14785
+ [...av.codecs.values()].filter(ci => ci.name === (av.type === 'audio' ? 'opus' : 'H264')).forEach(ci => {
14786
+ var _a;
14787
+ (_a = this.customCodecParameters.get(mediaType)) === null || _a === void 0 ? void 0 : _a.forEach((value, param) => {
14788
+ if (value === null) {
14789
+ ci.fmtParams.delete(param);
14790
+ } else {
14791
+ ci.fmtParams.set(param, "".concat(value));
14792
+ }
14793
+ });
14794
+ });
14795
+ }
14642
14796
  });
14643
14797
  if (getBrowserDetails().name !== 'Firefox') {
14644
14798
  setupBundle(parsed, this.options.bundlePolicy, this.midMap);
@@ -14688,18 +14842,53 @@ class MultistreamConnection extends EventEmitter {
14688
14842
  getPublishedTracks() {
14689
14843
  return [...this.sendTransceivers.values()].map(transceiver => transceiver.publishedTrack).filter(Boolean);
14690
14844
  }
14845
+ setCodecParameters(mediaType, parameters) {
14846
+ return __awaiter(this, void 0, void 0, function* () {
14847
+ var currentParams = this.customCodecParameters.get(mediaType) || new Map();
14848
+ Object.entries(parameters).forEach(_ref7 => {
14849
+ var [param, value] = _ref7;
14850
+ currentParams.set(param, value);
14851
+ });
14852
+ this.customCodecParameters.set(mediaType, currentParams);
14853
+ if (this.pc.getRemoteDescription()) {
14854
+ yield this.doLocalOfferAnswer();
14855
+ }
14856
+ });
14857
+ }
14858
+ deleteCodecParameters(mediaType, parameters) {
14859
+ return __awaiter(this, void 0, void 0, function* () {
14860
+ var currentParams = this.customCodecParameters.get(mediaType) || new Map();
14861
+ parameters.forEach(param => {
14862
+ currentParams.set(param, null);
14863
+ });
14864
+ this.customCodecParameters.set(mediaType, currentParams);
14865
+ if (this.pc.getRemoteDescription()) {
14866
+ yield this.doLocalOfferAnswer();
14867
+ }
14868
+ });
14869
+ }
14691
14870
  requestMedia(mediaType, mediaRequests) {
14692
14871
  var _a;
14693
14872
  var task = () => {
14873
+ var _a;
14694
14874
  var jmpSession = this.jmpSessions.get(mediaType);
14695
14875
  if (!jmpSession) {
14696
14876
  logger$4.error("Unable to find jmp session for ".concat(mediaType));
14697
14877
  return;
14698
14878
  }
14699
- if (!mediaRequests.every(mr => mr.receiveSlots.every(rs => rs.id))) {
14700
- logger$4.error("Running subscribe task, but ReceiveSlot ID is missing!");
14701
- return;
14702
- }
14879
+ var requestedReceiveSlotIds = [];
14880
+ mediaRequests.forEach(mr => mr.receiveSlots.forEach(rs => {
14881
+ if (!rs.id) {
14882
+ logger$4.error("Running subscribe task, but ReceiveSlot ID is missing!");
14883
+ return;
14884
+ }
14885
+ requestedReceiveSlotIds.push(rs.id);
14886
+ }));
14887
+ (_a = this.recvTransceivers.get(mediaType)) === null || _a === void 0 ? void 0 : _a.forEach(transceiver => {
14888
+ if (!requestedReceiveSlotIds.some(id => compareStreamIds(id, transceiver.receiveSlot.id))) {
14889
+ transceiver.receiveSlot._updateSource('no source', undefined);
14890
+ }
14891
+ });
14703
14892
  jmpSession.sendRequests(mediaRequests.map(mr => mr._toJmpScrRequest()));
14704
14893
  };
14705
14894
  if (((_a = this.dataChannel) === null || _a === void 0 ? void 0 : _a.readyState) === 'open') {
@@ -14722,7 +14911,7 @@ class MultistreamConnection extends EventEmitter {
14722
14911
  this.addMid(mediaType);
14723
14912
  transceiver.replaceTransceiver(this.pc.addTransceiver(toMediaStreamTrackKind(mediaType), {
14724
14913
  direction: 'sendrecv',
14725
- sendEncodings: getMediaFamily$1(mediaType) === MediaFamily.Video ? this.getVideoEncodingOptions() : undefined
14914
+ sendEncodings: getMediaFamily$1(mediaType) === MediaFamily.Video ? this.getVideoEncodingOptions(getMediaContent(mediaType)) : undefined
14726
14915
  }));
14727
14916
  transceiver.csi = generateCsi(getMediaFamily$1(mediaType), mainSceneId);
14728
14917
  (_a = this.jmpSessions.get(mediaType)) === null || _a === void 0 ? void 0 : _a.close();
@@ -14753,8 +14942,8 @@ class MultistreamConnection extends EventEmitter {
14753
14942
  }
14754
14943
  preProcessStats(stats) {
14755
14944
  return __awaiter(this, void 0, void 0, function* () {
14756
- yield Promise.all([...this.sendTransceivers.entries()].map(_ref7 => {
14757
- var [mediaType, transceiver] = _ref7;
14945
+ yield Promise.all([...this.sendTransceivers.entries()].map(_ref8 => {
14946
+ var [mediaType, transceiver] = _ref8;
14758
14947
  return __awaiter(this, void 0, void 0, function* () {
14759
14948
  (yield transceiver.getStats()).forEach(senderStats => {
14760
14949
  var _a;
@@ -14772,8 +14961,8 @@ class MultistreamConnection extends EventEmitter {
14772
14961
  });
14773
14962
  });
14774
14963
  }));
14775
- yield Promise.all([...this.recvTransceivers.entries()].map(_ref8 => {
14776
- var [mediaType, transceivers] = _ref8;
14964
+ yield Promise.all([...this.recvTransceivers.entries()].map(_ref9 => {
14965
+ var [mediaType, transceivers] = _ref9;
14777
14966
  return __awaiter(this, void 0, void 0, function* () {
14778
14967
  yield Promise.all(transceivers.map(transceiver => __awaiter(this, void 0, void 0, function* () {
14779
14968
  (yield transceiver.getStats()).forEach(receiverStats => {
@@ -23896,6 +24085,14 @@ class MultistreamRoapMediaConnection extends EventEmitter$4 {
23896
24085
  }
23897
24086
  this.roap.roapMessageReceived(roapMessage);
23898
24087
  }
24088
+ enableMultistreamAudio(enabled) {
24089
+ this.log('enableMultistreamAudio()', 'called');
24090
+ var sdpNegotiationNeeded = this.multistreamConnection.enableMultistreamAudio(enabled);
24091
+ if (sdpNegotiationNeeded) {
24092
+ return this.roap.initiateOffer();
24093
+ }
24094
+ return Promise.resolve();
24095
+ }
23899
24096
  publishTrack(track) {
23900
24097
  this.log('publishTrack()', 'called');
23901
24098
  return this.multistreamConnection.publishTrack(track);
package/dist/esm/index.js CHANGED
@@ -4915,6 +4915,10 @@ var LocalTrackEvents;
4915
4915
  * Fires when there has been a change in the underlying track.
4916
4916
  */
4917
4917
  LocalTrackEvents["UnderlyingTrackChange"] = "underlying-track-change";
4918
+ /**
4919
+ * Fires when the applyConstraints() has been called for the track.
4920
+ */
4921
+ LocalTrackEvents["TrackConstraintsChange"] = "track-constraints-change";
4918
4922
  })(LocalTrackEvents || (LocalTrackEvents = {}));
4919
4923
  // TBD: Fix this once types are published separately
4920
4924
  // export type TrackEffect = BaseMicrophoneEffect | BaseCameraEffect;
@@ -5103,7 +5107,7 @@ class LocalTrack extends EventEmitter$2 {
5103
5107
  return effect;
5104
5108
  }
5105
5109
  /**
5106
- * Cleanup local microphone track.
5110
+ * Cleanup the local microphone track.
5107
5111
  */
5108
5112
  disposeEffects() {
5109
5113
  if (this.effects.size > 0) {
@@ -5113,6 +5117,57 @@ class LocalTrack extends EventEmitter$2 {
5113
5117
  this.emit(LocalTrackEvents.UnderlyingTrackChange);
5114
5118
  }
5115
5119
  }
5120
+ /**
5121
+ * Apply constraints to the track.
5122
+ *
5123
+ * @param constraints - The constraints to apply to the track.
5124
+ * @returns A promise which resolves when the constraints have been successfully applied.
5125
+ */
5126
+ applyConstraints(constraints) {
5127
+ return __awaiter$1(this, void 0, void 0, function* () {
5128
+ logger$3.log("Applying constraints to local track:", constraints);
5129
+ var ret = this.underlyingTrack.applyConstraints(constraints).then(() => {
5130
+ this.emit(LocalTrackEvents.TrackConstraintsChange);
5131
+ });
5132
+ return ret;
5133
+ });
5134
+ }
5135
+ /**
5136
+ * Get the current constraints of the track.
5137
+ *
5138
+ * @returns The constraints of the track.
5139
+ */
5140
+ getConstraints() {
5141
+ return this.underlyingTrack.getConstraints();
5142
+ }
5143
+ /**
5144
+ * Get the current settings of the track.
5145
+ *
5146
+ * @returns The settings of the track.
5147
+ */
5148
+ getSettings() {
5149
+ return this.underlyingTrack.getSettings();
5150
+ }
5151
+ /**
5152
+ * Check the resolution and then return how many layers will be active.
5153
+ *
5154
+ * @returns The active layers count.
5155
+ */
5156
+ getNumActiveSimulcastLayers() {
5157
+ var activeSimulcastLayersNumber = 0;
5158
+ if (this.trackState.type === 'audio') {
5159
+ return activeSimulcastLayersNumber;
5160
+ }
5161
+ var videoHeight = this.underlyingTrack.getSettings().height;
5162
+ if (videoHeight <= 180) {
5163
+ activeSimulcastLayersNumber = 1;
5164
+ } else if (videoHeight <= 360) {
5165
+ activeSimulcastLayersNumber = 2;
5166
+ } else {
5167
+ activeSimulcastLayersNumber = 3;
5168
+ }
5169
+ return activeSimulcastLayersNumber;
5170
+ }
5116
5171
  }
5117
5172
  LocalTrack.Events = LocalTrackEvents;
5118
5173
 
@@ -8541,6 +8596,44 @@ class PeerConnection extends EventEmitter$2 {
8541
8596
  get iceGatheringState() {
8542
8597
  return this.pc.iceGatheringState;
8543
8598
  }
8599
+ /**
8600
+ * Returns the type of a connection that has been established.
8601
+ *
8602
+ * @returns The connection type which would be `ConnectionType`.
8603
+ */
8604
+ getCurrentConnectionType() {
8605
+ var _a;
8606
+ return __awaiter$1(this, void 0, void 0, function* () {
8607
+ // make sure this method only can be called when the ice connection is established;
8608
+ var isIceConnected = this.pc.iceConnectionState === 'connected' || this.pc.iceConnectionState === 'completed';
8609
+ if (!isIceConnected) {
8610
+ throw new Error('Ice connection is not established');
8611
+ }
8612
+ var succeededLocalCandidateIds = new Set();
8613
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8614
+ var localCandidateStatsReports = [];
8615
+ (yield this.pc.getStats()).forEach(report => {
8616
+ var _a;
8617
+ // collect all local candidate ids from `candidate-pair` stats reports with `succeeded` state.
8618
+ if (report.type === 'candidate-pair' && ((_a = report.state) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'succeeded') {
8619
+ succeededLocalCandidateIds.add(report.localCandidateId);
8620
+ }
8621
+ // collect all `local-candidate` stats.
8622
+ if (report.type === 'local-candidate') {
8623
+ localCandidateStatsReports.push(report);
8624
+ }
8625
+ });
8626
+ // find the `local-candidate` stats which report id contains in `succeededLocalCandidateIds`.
8627
+ var localCandidate = localCandidateStatsReports.find(report => succeededLocalCandidateIds.has(report.id));
8628
+ if (!localCandidate) {
8629
+ return 'unknown';
8630
+ }
8631
+ if (localCandidate.relayProtocol) {
8632
+ return "TURN-".concat(localCandidate.relayProtocol.toUpperCase());
8633
+ }
8634
+ return (_a = localCandidate.protocol) === null || _a === void 0 ? void 0 : _a.toUpperCase();
8635
+ });
8636
+ }
8544
8637
  }
8545
8638
  PeerConnection.Events = PeerConnectionEvents;
8546
8639
 
@@ -12121,7 +12214,7 @@ function injectContentTypes(sdp, contentTypeMap) {
12121
12214
  });
12122
12215
  }
12123
12216
  function injectJmpAttributes(parsedSdp, csiMap, streamSignalingMode) {
12124
- parsedSdp.avMedia.filter(mLine => mLine.direction === 'sendrecv' || mLine.direction === 'sendonly').forEach(mLine => {
12217
+ parsedSdp.avMedia.filter(mLine => mLine.direction === 'sendrecv' || mLine.direction === 'inactive').forEach(mLine => {
12125
12218
  if (!mLine.otherLines.find(line => line instanceof JmpLine)) {
12126
12219
  mLine.addLine(new JmpLine());
12127
12220
  }
@@ -12138,11 +12231,15 @@ function injectJmpAttributes(parsedSdp, csiMap, streamSignalingMode) {
12138
12231
  }
12139
12232
  });
12140
12233
  }
12234
+ function hasSimulcast(av) {
12235
+ return !!av.simulcast || av.ssrcGroups.map(sg => sg.semantics).some(sem => sem === 'SIM');
12236
+ }
12141
12237
  class SendOnlyTransceiver extends Transceiver {
12142
12238
  constructor(rtcpRtpTransceiver, csi) {
12143
12239
  super(rtcpRtpTransceiver);
12144
12240
  this.requested = false;
12145
12241
  this.csi = csi;
12242
+ this.direction = 'sendrecv';
12146
12243
  this.handleTrackChange = this.handleTrackChange.bind(this);
12147
12244
  }
12148
12245
  handleTrackChange() {
@@ -12162,6 +12259,7 @@ class SendOnlyTransceiver extends Transceiver {
12162
12259
  var _a;
12163
12260
  return __awaiter(this, void 0, void 0, function* () {
12164
12261
  _super.replaceTransceiver.call(this, newRtcRtpTransceiver);
12262
+ newRtcRtpTransceiver.direction = this.direction;
12165
12263
  if (this.requested) {
12166
12264
  yield this.sender.replaceTrack(((_a = this.publishedTrack) === null || _a === void 0 ? void 0 : _a.underlyingTrack) || null);
12167
12265
  }
@@ -12231,8 +12329,8 @@ class SendOnlyTransceiver extends Transceiver {
12231
12329
  return this.replacePublishedTrack();
12232
12330
  }
12233
12331
  setActive(enabled) {
12234
- var direction = enabled ? 'sendrecv' : 'inactive';
12235
- this._rtcRtpTransceiver.direction = direction;
12332
+ this.direction = enabled ? 'sendrecv' : 'inactive';
12333
+ this._rtcRtpTransceiver.direction = this.direction;
12236
12334
  return this._rtcRtpTransceiver.direction !== this._rtcRtpTransceiver.currentDirection;
12237
12335
  }
12238
12336
  getStats() {
@@ -14130,11 +14228,15 @@ var defaultMultistreamConnectionOptions = {
14130
14228
  disableSimulcast: getBrowserDetails().name === 'Firefox',
14131
14229
  streamSignalingMode: 'SSRC',
14132
14230
  bundlePolicy: 'max-compat',
14133
- iceServers: undefined
14231
+ iceServers: undefined,
14232
+ disableContentSimulcast: true,
14233
+ enableMainAudio: true,
14234
+ enableMainVideo: true
14134
14235
  };
14135
14236
  class MultistreamConnection extends EventEmitter {
14136
14237
  constructor() {
14137
14238
  var userOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
14239
+ var _a, _b;
14138
14240
  super();
14139
14241
  this.sendTransceivers = new Map();
14140
14242
  this.recvTransceivers = new Map();
@@ -14142,6 +14244,7 @@ class MultistreamConnection extends EventEmitter {
14142
14244
  this.pendingJmpTasks = [];
14143
14245
  this.metricsCallback = () => {};
14144
14246
  this.overuseUpdateCallback = () => {};
14247
+ this.customCodecParameters = new Map();
14145
14248
  this.midMap = new Map();
14146
14249
  this.currentMid = 0;
14147
14250
  this.options = Object.assign(Object.assign({}, defaultMultistreamConnectionOptions), userOptions);
@@ -14152,11 +14255,13 @@ class MultistreamConnection extends EventEmitter {
14152
14255
  this.overuseStateManager.start();
14153
14256
  this.statsManager = new StatsManager(() => this.pc.getStats(), stats => this.preProcessStats(stats));
14154
14257
  var mainSceneId = generateSceneId();
14155
- var videoMainEncodingOptions = this.getVideoEncodingOptions();
14258
+ var videoMainEncodingOptions = this.getVideoEncodingOptions(MediaContent$1.Main);
14156
14259
  this.createSendTransceiver(MediaType$1.VideoMain, mainSceneId, videoMainEncodingOptions);
14157
14260
  this.createSendTransceiver(MediaType$1.AudioMain, mainSceneId);
14261
+ (_a = this.sendTransceivers.get(MediaType$1.VideoMain)) === null || _a === void 0 ? void 0 : _a.setActive(this.options.enableMainVideo);
14262
+ (_b = this.sendTransceivers.get(MediaType$1.AudioMain)) === null || _b === void 0 ? void 0 : _b.setActive(this.options.enableMainAudio);
14158
14263
  if (this.options.floorControlledPresentation) {
14159
- var videoPresentationEncodingOptions = this.getVideoEncodingOptions();
14264
+ var videoPresentationEncodingOptions = this.getVideoEncodingOptions(MediaContent$1.Slides);
14160
14265
  var contentSceneId = generateSceneId();
14161
14266
  this.createSendTransceiver(MediaType$1.VideoSlides, contentSceneId, videoPresentationEncodingOptions);
14162
14267
  this.createSendTransceiver(MediaType$1.AudioSlides, contentSceneId);
@@ -14186,8 +14291,9 @@ class MultistreamConnection extends EventEmitter {
14186
14291
  getConnectionState() {
14187
14292
  return this.pc.getConnectionState();
14188
14293
  }
14189
- getVideoEncodingOptions() {
14190
- return !this.options.disableSimulcast ? [{
14294
+ getVideoEncodingOptions(content) {
14295
+ var enabledSimulcast = content === MediaContent$1.Main ? !this.options.disableSimulcast : !this.options.disableContentSimulcast;
14296
+ return enabledSimulcast ? [{
14191
14297
  scaleResolutionDownBy: 4,
14192
14298
  active: false
14193
14299
  }, {
@@ -14275,9 +14381,11 @@ class MultistreamConnection extends EventEmitter {
14275
14381
  this.jmpSessions.set(mediaType, jmpSession);
14276
14382
  }
14277
14383
  sendSourceWarnings(mediaType, requests) {
14384
+ var _a;
14278
14385
  if (getMediaFamily$1(mediaType) === MediaFamily$1.Video) {
14279
14386
  var sendTransceiver = this.getSendTransceiverOrThrow(mediaType);
14280
14387
  var signaler = this.streamSignalerManager.getEgressStreamSignalerOrThrow(sendTransceiver.mid);
14388
+ var activeSimulcastLayerNumber = ((_a = sendTransceiver.publishedTrack) === null || _a === void 0 ? void 0 : _a.getNumActiveSimulcastLayers()) || 0;
14281
14389
  var sourceWarnings = [];
14282
14390
  requests.forEach(_ref5 => {
14283
14391
  var {
@@ -14297,6 +14405,12 @@ class MultistreamConnection extends EventEmitter {
14297
14405
  state: 'invalid source',
14298
14406
  csi: sendTransceiver.csi
14299
14407
  });
14408
+ } else if (signaler.getEncodingIndexForStreamId(id) > activeSimulcastLayerNumber) {
14409
+ sourceWarnings.push({
14410
+ id,
14411
+ state: 'no source',
14412
+ csi: sendTransceiver.csi
14413
+ });
14300
14414
  }
14301
14415
  });
14302
14416
  });
@@ -14473,22 +14587,19 @@ class MultistreamConnection extends EventEmitter {
14473
14587
  });
14474
14588
  }
14475
14589
  addTrackListeners(mediaType, track) {
14476
- var onTrackMute = event => {
14477
- var sendTransceiver = this.getSendTransceiverOrThrow(mediaType);
14478
- var signaler = this.streamSignalerManager.getEgressStreamSignaler(sendTransceiver.mid);
14479
- if (!signaler) {
14480
- return;
14590
+ var onTrackResolutionChange = () => {
14591
+ var sources = this.getVideoSources(mediaType);
14592
+ if (sources != null) {
14593
+ this.sendSourceIndication(mediaType, 1, sources);
14481
14594
  }
14482
- if (this.getPublishedTracks().includes(track)) {
14483
- if (getMediaFamily$1(mediaType) === MediaFamily$1.Audio) {
14484
- this.sendSourceIndication(mediaType, +!event.trackState.muted);
14485
- } else {
14486
- var state = event.trackState.muted ? 'avatar' : 'live';
14487
- var sources = signaler.getSenderIds().map(id => ({
14488
- id,
14489
- state,
14490
- csi: sendTransceiver.csi
14491
- }));
14595
+ };
14596
+ track.on(LocalTrack.Events.TrackConstraintsChange, onTrackResolutionChange);
14597
+ var onTrackMute = event => {
14598
+ if (getMediaFamily$1(mediaType) === MediaFamily$1.Audio) {
14599
+ this.sendSourceIndication(mediaType, +!event.trackState.muted);
14600
+ } else {
14601
+ var sources = this.getVideoSources(mediaType);
14602
+ if (sources != null) {
14492
14603
  this.sendSourceIndication(mediaType, +!event.trackState.muted, sources);
14493
14604
  }
14494
14605
  }
@@ -14498,28 +14609,47 @@ class MultistreamConnection extends EventEmitter {
14498
14609
  if (!event.isPublished) {
14499
14610
  track.off(LocalTrack.Events.Muted, onTrackMute);
14500
14611
  track.off(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);
14612
+ track.off(LocalTrack.Events.TrackConstraintsChange, onTrackResolutionChange);
14501
14613
  }
14502
- var sendTransceiver = this.getSendTransceiverOrThrow(mediaType);
14503
- var signaler = this.streamSignalerManager.getEgressStreamSignaler(sendTransceiver.mid);
14504
- if (!signaler) {
14505
- return;
14506
- }
14507
- if (!event.trackState.muted) {
14508
- if (getMediaFamily$1(mediaType) === MediaFamily$1.Audio) {
14509
- this.sendSourceIndication(mediaType, +event.isPublished);
14510
- } else {
14511
- var state = event.isPublished ? 'live' : 'no source';
14512
- var sources = signaler.getSenderIds().map(id => ({
14513
- id,
14514
- state,
14515
- csi: sendTransceiver.csi
14516
- }));
14614
+ if (getMediaFamily$1(mediaType) === MediaFamily$1.Audio) {
14615
+ this.sendSourceIndication(mediaType, +event.isPublished);
14616
+ } else {
14617
+ var sources = this.getVideoSources(mediaType);
14618
+ if (sources != null) {
14517
14619
  this.sendSourceIndication(mediaType, +event.isPublished, sources);
14518
14620
  }
14519
14621
  }
14520
14622
  };
14521
14623
  track.on(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);
14522
14624
  }
14625
+ getVideoSources(mediaType) {
14626
+ var _a, _b, _c;
14627
+ var sendTransceiver = this.getSendTransceiverOrThrow(mediaType);
14628
+ var signaler = this.streamSignalerManager.getEgressStreamSignaler(sendTransceiver.mid);
14629
+ if (!signaler) {
14630
+ return null;
14631
+ }
14632
+ var activeSimulcastLayerNumber = ((_a = sendTransceiver.publishedTrack) === null || _a === void 0 ? void 0 : _a.getNumActiveSimulcastLayers()) || 0;
14633
+ var published = (_b = sendTransceiver.publishedTrack) === null || _b === void 0 ? void 0 : _b.published;
14634
+ var muted = ((_c = sendTransceiver.publishedTrack) === null || _c === void 0 ? void 0 : _c.muted) === true;
14635
+ return signaler.getSenderIds().map(id => {
14636
+ var state;
14637
+ if (!published) {
14638
+ state = 'no source';
14639
+ } else if (muted) {
14640
+ state = 'avatar';
14641
+ } else if (activeSimulcastLayerNumber <= signaler.getEncodingIndexForStreamId(id)) {
14642
+ state = 'no source';
14643
+ } else {
14644
+ state = 'live';
14645
+ }
14646
+ return {
14647
+ id,
14648
+ state,
14649
+ csi: sendTransceiver.csi
14650
+ };
14651
+ });
14652
+ }
14523
14653
  createReceiveSlot(mediaType) {
14524
14654
  return __awaiter(this, void 0, void 0, function* () {
14525
14655
  var rtcRtpTransceiver = this.pc.addTransceiver(toMediaStreamTrackKind(mediaType), {
@@ -14534,20 +14664,7 @@ class MultistreamConnection extends EventEmitter {
14534
14664
  return ingressSignaler.getReceiverId();
14535
14665
  });
14536
14666
  if (this.pc.getRemoteDescription()) {
14537
- yield this.pc.createOffer().then(offer => {
14538
- if (!offer.sdp) {
14539
- throw new Error('No SDP offer');
14540
- }
14541
- offer.sdp = this.preProcessLocalOffer(offer.sdp);
14542
- return this.pc.setLocalDescription(offer);
14543
- }).then(() => {
14544
- var _a;
14545
- var answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
14546
- return this.pc.setRemoteDescription({
14547
- type: 'answer',
14548
- sdp: answer
14549
- });
14550
- });
14667
+ yield this.doLocalOfferAnswer();
14551
14668
  }
14552
14669
  this.recvTransceivers.set(mediaType, [...(this.recvTransceivers.get(mediaType) || []), recvOnlyTransceiver]);
14553
14670
  return recvOnlyTransceiver.receiveSlot;
@@ -14595,10 +14712,31 @@ class MultistreamConnection extends EventEmitter {
14595
14712
  }
14596
14713
  setAnswer(answer) {
14597
14714
  return __awaiter(this, void 0, void 0, function* () {
14715
+ var isInitialAnswer = !this.pc.getRemoteDescription();
14598
14716
  var sdp = this.preProcessRemoteAnswer(answer);
14599
14717
  return this.pc.setRemoteDescription({
14600
14718
  type: 'answer',
14601
14719
  sdp
14720
+ }).then(() => {
14721
+ if (isInitialAnswer && this.customCodecParameters.size > 0) {
14722
+ this.doLocalOfferAnswer();
14723
+ }
14724
+ });
14725
+ });
14726
+ }
14727
+ doLocalOfferAnswer() {
14728
+ var _a;
14729
+ return __awaiter(this, void 0, void 0, function* () {
14730
+ var offer = yield this.pc.createOffer();
14731
+ if (!offer.sdp) {
14732
+ throw new Error('No SDP offer');
14733
+ }
14734
+ offer.sdp = this.preProcessLocalOffer(offer.sdp);
14735
+ yield this.pc.setLocalDescription(offer);
14736
+ var answer = this.preProcessRemoteAnswer((_a = this.pc.getRemoteDescription()) === null || _a === void 0 ? void 0 : _a.sdp);
14737
+ return this.pc.setRemoteDescription({
14738
+ type: 'answer',
14739
+ sdp: answer
14602
14740
  });
14603
14741
  });
14604
14742
  }
@@ -14619,7 +14757,7 @@ class MultistreamConnection extends EventEmitter {
14619
14757
  });
14620
14758
  parsed.avMedia.filter(av => av.direction === 'sendrecv').forEach(av => {
14621
14759
  var egressSignaler = this.streamSignalerManager.getOrCreateEgressStreamSignaler(av.mid);
14622
- var simulcastEnabled = av.type === 'video' ? !this.options.disableSimulcast : false;
14760
+ var simulcastEnabled = hasSimulcast(av);
14623
14761
  var rtxEnabled = av.type === 'video';
14624
14762
  egressSignaler.signalStreams(simulcastEnabled, rtxEnabled, av);
14625
14763
  if (av.type === 'video') {
@@ -14628,6 +14766,22 @@ class MultistreamConnection extends EventEmitter {
14628
14766
  ci.fmtParams.set('max-fs', "".concat(defaultMaxVideoEncodeFrameSize));
14629
14767
  });
14630
14768
  }
14769
+ var mediaType = [...this.sendTransceivers.keys()].find(key => {
14770
+ var _a;
14771
+ return ((_a = this.sendTransceivers.get(key)) === null || _a === void 0 ? void 0 : _a.mid) === av.mid;
14772
+ });
14773
+ if (mediaType && this.customCodecParameters.has(mediaType)) {
14774
+ [...av.codecs.values()].filter(ci => ci.name === (av.type === 'audio' ? 'opus' : 'H264')).forEach(ci => {
14775
+ var _a;
14776
+ (_a = this.customCodecParameters.get(mediaType)) === null || _a === void 0 ? void 0 : _a.forEach((value, param) => {
14777
+ if (value === null) {
14778
+ ci.fmtParams.delete(param);
14779
+ } else {
14780
+ ci.fmtParams.set(param, "".concat(value));
14781
+ }
14782
+ });
14783
+ });
14784
+ }
14631
14785
  });
14632
14786
  if (getBrowserDetails().name !== 'Firefox') {
14633
14787
  setupBundle(parsed, this.options.bundlePolicy, this.midMap);
@@ -14677,18 +14831,53 @@ class MultistreamConnection extends EventEmitter {
14677
14831
  getPublishedTracks() {
14678
14832
  return [...this.sendTransceivers.values()].map(transceiver => transceiver.publishedTrack).filter(Boolean);
14679
14833
  }
14834
+ setCodecParameters(mediaType, parameters) {
14835
+ return __awaiter(this, void 0, void 0, function* () {
14836
+ var currentParams = this.customCodecParameters.get(mediaType) || new Map();
14837
+ Object.entries(parameters).forEach(_ref7 => {
14838
+ var [param, value] = _ref7;
14839
+ currentParams.set(param, value);
14840
+ });
14841
+ this.customCodecParameters.set(mediaType, currentParams);
14842
+ if (this.pc.getRemoteDescription()) {
14843
+ yield this.doLocalOfferAnswer();
14844
+ }
14845
+ });
14846
+ }
14847
+ deleteCodecParameters(mediaType, parameters) {
14848
+ return __awaiter(this, void 0, void 0, function* () {
14849
+ var currentParams = this.customCodecParameters.get(mediaType) || new Map();
14850
+ parameters.forEach(param => {
14851
+ currentParams.set(param, null);
14852
+ });
14853
+ this.customCodecParameters.set(mediaType, currentParams);
14854
+ if (this.pc.getRemoteDescription()) {
14855
+ yield this.doLocalOfferAnswer();
14856
+ }
14857
+ });
14858
+ }
14680
14859
  requestMedia(mediaType, mediaRequests) {
14681
14860
  var _a;
14682
14861
  var task = () => {
14862
+ var _a;
14683
14863
  var jmpSession = this.jmpSessions.get(mediaType);
14684
14864
  if (!jmpSession) {
14685
14865
  logger$4.error("Unable to find jmp session for ".concat(mediaType));
14686
14866
  return;
14687
14867
  }
14688
- if (!mediaRequests.every(mr => mr.receiveSlots.every(rs => rs.id))) {
14689
- logger$4.error("Running subscribe task, but ReceiveSlot ID is missing!");
14690
- return;
14691
- }
14868
+ var requestedReceiveSlotIds = [];
14869
+ mediaRequests.forEach(mr => mr.receiveSlots.forEach(rs => {
14870
+ if (!rs.id) {
14871
+ logger$4.error("Running subscribe task, but ReceiveSlot ID is missing!");
14872
+ return;
14873
+ }
14874
+ requestedReceiveSlotIds.push(rs.id);
14875
+ }));
14876
+ (_a = this.recvTransceivers.get(mediaType)) === null || _a === void 0 ? void 0 : _a.forEach(transceiver => {
14877
+ if (!requestedReceiveSlotIds.some(id => compareStreamIds(id, transceiver.receiveSlot.id))) {
14878
+ transceiver.receiveSlot._updateSource('no source', undefined);
14879
+ }
14880
+ });
14692
14881
  jmpSession.sendRequests(mediaRequests.map(mr => mr._toJmpScrRequest()));
14693
14882
  };
14694
14883
  if (((_a = this.dataChannel) === null || _a === void 0 ? void 0 : _a.readyState) === 'open') {
@@ -14711,7 +14900,7 @@ class MultistreamConnection extends EventEmitter {
14711
14900
  this.addMid(mediaType);
14712
14901
  transceiver.replaceTransceiver(this.pc.addTransceiver(toMediaStreamTrackKind(mediaType), {
14713
14902
  direction: 'sendrecv',
14714
- sendEncodings: getMediaFamily$1(mediaType) === MediaFamily$1.Video ? this.getVideoEncodingOptions() : undefined
14903
+ sendEncodings: getMediaFamily$1(mediaType) === MediaFamily$1.Video ? this.getVideoEncodingOptions(getMediaContent(mediaType)) : undefined
14715
14904
  }));
14716
14905
  transceiver.csi = generateCsi(getMediaFamily$1(mediaType), mainSceneId);
14717
14906
  (_a = this.jmpSessions.get(mediaType)) === null || _a === void 0 ? void 0 : _a.close();
@@ -14742,8 +14931,8 @@ class MultistreamConnection extends EventEmitter {
14742
14931
  }
14743
14932
  preProcessStats(stats) {
14744
14933
  return __awaiter(this, void 0, void 0, function* () {
14745
- yield Promise.all([...this.sendTransceivers.entries()].map(_ref7 => {
14746
- var [mediaType, transceiver] = _ref7;
14934
+ yield Promise.all([...this.sendTransceivers.entries()].map(_ref8 => {
14935
+ var [mediaType, transceiver] = _ref8;
14747
14936
  return __awaiter(this, void 0, void 0, function* () {
14748
14937
  (yield transceiver.getStats()).forEach(senderStats => {
14749
14938
  var _a;
@@ -14761,8 +14950,8 @@ class MultistreamConnection extends EventEmitter {
14761
14950
  });
14762
14951
  });
14763
14952
  }));
14764
- yield Promise.all([...this.recvTransceivers.entries()].map(_ref8 => {
14765
- var [mediaType, transceivers] = _ref8;
14953
+ yield Promise.all([...this.recvTransceivers.entries()].map(_ref9 => {
14954
+ var [mediaType, transceivers] = _ref9;
14766
14955
  return __awaiter(this, void 0, void 0, function* () {
14767
14956
  yield Promise.all(transceivers.map(transceiver => __awaiter(this, void 0, void 0, function* () {
14768
14957
  (yield transceiver.getStats()).forEach(receiverStats => {
@@ -23885,6 +24074,14 @@ class MultistreamRoapMediaConnection extends EventEmitter$4 {
23885
24074
  }
23886
24075
  this.roap.roapMessageReceived(roapMessage);
23887
24076
  }
24077
+ enableMultistreamAudio(enabled) {
24078
+ this.log('enableMultistreamAudio()', 'called');
24079
+ var sdpNegotiationNeeded = this.multistreamConnection.enableMultistreamAudio(enabled);
24080
+ if (sdpNegotiationNeeded) {
24081
+ return this.roap.initiateOffer();
24082
+ }
24083
+ return Promise.resolve();
24084
+ }
23888
24085
  publishTrack(track) {
23889
24086
  this.log('publishTrack()', 'called');
23890
24087
  return this.multistreamConnection.publishTrack(track);
@@ -27,6 +27,7 @@ export declare class MultistreamRoapMediaConnection extends EventEmitter {
27
27
  getStats(): Promise<RTCStatsReport>;
28
28
  getTransceiverStats(): Promise<TransceiverStats>;
29
29
  roapMessageReceived(roapMessage: RoapMessage): void;
30
+ enableMultistreamAudio(enabled: boolean): Promise<void>;
30
31
  publishTrack(track: LocalTrack): Promise<void>;
31
32
  unpublishTrack(track: LocalTrack): Promise<void>;
32
33
  createReceiveSlot(mediaType: MediaType): Promise<ReceiveSlot>;
@@ -1 +1 @@
1
- {"version":3,"file":"MultistreamRoapMediaConnection.d.ts","sourceRoot":"","sources":["../../../src/MediaConnection/MultistreamRoapMediaConnection.ts"],"names":[],"mappings":";AAAA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAElC,OAAO,EAEL,UAAU,EACV,YAAY,EAGZ,WAAW,EACX,gBAAgB,EACjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAA6C,SAAS,EAAC,MAAM,yBAAyB,CAAC;AAG9F,OAAO,EAAQ,eAAe,EAAE,WAAW,EAAmB,MAAM,cAAc,CAAC;AAKnF,OAAO,EAAC,2BAA2B,EAAC,MAAM,UAAU,CAAC;AAGrD,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,cAAc,EACd,SAAS,EACT,WAAW,EACX,SAAS,EACT,WAAW,EACX,MAAM,EACN,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AAGjC,qBAAa,8BAA+B,SAAQ,YAAY;IAC9D,OAAO,CAAC,EAAE,CAAC,CAAS;IAEpB,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB,OAAO,CAAC,qBAAqB,CAAwB;IAErD,OAAO,CAAC,IAAI,CAAO;IAEnB,OAAO,CAAC,qBAAqB,CAAS;gBAS1B,qBAAqB,EAAE,2BAA2B,EAAE,OAAO,CAAC,EAAE,MAAM;IAehF,OAAO,CAAC,mBAAmB;IAqC3B,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,2BAA2B;IAyCnC,OAAO,CAAC,UAAU;IA4BX,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB9B,KAAK,IAAI,IAAI;IAOpB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,eAAe;IAiBhB,SAAS,CAAC,UAAU,EAAE,YAAY,EAAE,EAAE,aAAa,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B1E,kBAAkB,IAAI,eAAe;IAWrC,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAOnC,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAShD,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAanD,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9C,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhD,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAM7D,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,IAAI;IAM9E,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,kBAAkB;CAkB3B"}
1
+ {"version":3,"file":"MultistreamRoapMediaConnection.d.ts","sourceRoot":"","sources":["../../../src/MediaConnection/MultistreamRoapMediaConnection.ts"],"names":[],"mappings":";AAAA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAElC,OAAO,EAEL,UAAU,EACV,YAAY,EAGZ,WAAW,EACX,gBAAgB,EACjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAA6C,SAAS,EAAC,MAAM,yBAAyB,CAAC;AAG9F,OAAO,EAAQ,eAAe,EAAE,WAAW,EAAmB,MAAM,cAAc,CAAC;AAKnF,OAAO,EAAC,2BAA2B,EAAC,MAAM,UAAU,CAAC;AAGrD,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,cAAc,EACd,SAAS,EACT,WAAW,EACX,SAAS,EACT,WAAW,EACX,MAAM,EACN,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AAGjC,qBAAa,8BAA+B,SAAQ,YAAY;IAC9D,OAAO,CAAC,EAAE,CAAC,CAAS;IAEpB,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB,OAAO,CAAC,qBAAqB,CAAwB;IAErD,OAAO,CAAC,IAAI,CAAO;IAEnB,OAAO,CAAC,qBAAqB,CAAS;gBAS1B,qBAAqB,EAAE,2BAA2B,EAAE,OAAO,CAAC,EAAE,MAAM;IAehF,OAAO,CAAC,mBAAmB;IAqC3B,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,2BAA2B;IAyCnC,OAAO,CAAC,UAAU;IA4BX,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB9B,KAAK,IAAI,IAAI;IAOpB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,eAAe;IAiBhB,SAAS,CAAC,UAAU,EAAE,YAAY,EAAE,EAAE,aAAa,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B1E,kBAAkB,IAAI,eAAe;IAWrC,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAOnC,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAShD,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAanD,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvD,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9C,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhD,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAM7D,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,IAAI;IAM9E,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,kBAAkB;CAkB3B"}
@@ -20,5 +20,7 @@ export interface MediaConnectionConfig {
20
20
  }
21
21
  export interface MultistreamConnectionConfig {
22
22
  iceServers: Array<RTCIceServer>;
23
+ enableMainAudio: boolean;
24
+ enableMainVideo: boolean;
23
25
  }
24
26
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/MediaConnection/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAoE;IAC7F,gBAAgB,CAAC,EAAE,OAAO,CAAuE;IACjG,kBAAkB,CAAC,EAAE,OAAO,CAAoE;IAChG,eAAe,CAAC,EAAE;QAChB,KAAK,EAAE,MAAM,CAA6E;QAC1F,KAAK,EAAE,MAAM,CAA6E;KAC3F,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CACmE;IACxF,iBAAiB,CAAC,EAAE,MAAM,CAAqE;IAC/F,aAAa,CAAC,EAAE,OAAO,CACkC;IACzD,UAAU,CAAC,EAAE,OAAO,CAC4C;IAChE,SAAS,CAAC,EAAE,MAAM,CAEkD;CACrE;AAED,MAAM,WAAW,qBAAqB;IAEpC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAChC,wBAAwB,EAAE,OAAO,CAmBM;IACvC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,WAAW,2BAA2B;IAE1C,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CACjC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/MediaConnection/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAoE;IAC7F,gBAAgB,CAAC,EAAE,OAAO,CAAuE;IACjG,kBAAkB,CAAC,EAAE,OAAO,CAAoE;IAChG,eAAe,CAAC,EAAE;QAChB,KAAK,EAAE,MAAM,CAA6E;QAC1F,KAAK,EAAE,MAAM,CAA6E;KAC3F,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CACmE;IACxF,iBAAiB,CAAC,EAAE,MAAM,CAAqE;IAC/F,aAAa,CAAC,EAAE,OAAO,CACkC;IACzD,UAAU,CAAC,EAAE,OAAO,CAC4C;IAChE,SAAS,CAAC,EAAE,MAAM,CAEkD;CACrE;AAED,MAAM,WAAW,qBAAqB;IAEpC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAChC,wBAAwB,EAAE,OAAO,CAmBM;IACvC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,WAAW,2BAA2B;IAC1C,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;CAC1B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webex/internal-media-core",
3
- "version": "1.35.1",
3
+ "version": "1.35.3",
4
4
  "files": [
5
5
  "dist/cjs",
6
6
  "dist/esm",
@@ -47,7 +47,7 @@
47
47
  "@babel/runtime": "^7.18.9",
48
48
  "@webex/json-multistream": "1.20.2",
49
49
  "@webex/ts-sdp": "1.3.0",
50
- "@webex/web-client-media-engine": "1.38.1",
50
+ "@webex/web-client-media-engine": "1.40.2",
51
51
  "detectrtc": "^1.4.1",
52
52
  "events": "^3.3.0",
53
53
  "typed-emitter": "^2.1.0",