@webex/web-client-media-engine 1.40.4 → 1.40.5

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
@@ -543,12 +543,13 @@ function createMicrophoneTrack(constructor, constraints) {
543
543
  * Creates a display video track.
544
544
  *
545
545
  * @param constructor - Constructor for the local display track.
546
+ * @param videoContentHint - An optional parameters to give a hint for the content of the track.
546
547
  * @returns A Promise that resolves to a LocalDisplayTrack.
547
548
  */
548
- function createDisplayTrack(constructor) {
549
+ function createDisplayTrack(constructor, videoContentHint) {
549
550
  return __awaiter$1(this, void 0, void 0, function* () {
550
551
  const stream = yield getDisplayMedia({ video: true });
551
- return new constructor(stream);
552
+ return new constructor(stream, videoContentHint);
552
553
  });
553
554
  }
554
555
  /**
@@ -1354,6 +1355,25 @@ class LocalCameraTrack extends LocalTrack {
1354
1355
  * Represents a local track for a display source.
1355
1356
  */
1356
1357
  class LocalDisplayTrack extends LocalTrack {
1358
+ /**
1359
+ * Create a LocalDisplayTrack from the given values.
1360
+ *
1361
+ * @param stream - The MediaStream for this track.
1362
+ * @param videoContentHint - An optional content hint, describing the content of the track.
1363
+ */
1364
+ constructor(stream, videoContentHint) {
1365
+ super(stream);
1366
+ this._videoContentHint = videoContentHint;
1367
+ this.underlyingTrack.contentHint = videoContentHint || '';
1368
+ }
1369
+ /**
1370
+ * Get the VideoContentHint for this track.
1371
+ *
1372
+ * @returns The VideoContentHint for this track.
1373
+ */
1374
+ get videoContentHint() {
1375
+ return this._videoContentHint;
1376
+ }
1357
1377
  }
1358
1378
 
1359
1379
  /**
@@ -5705,11 +5725,12 @@ function compareStreamIds(id1, id2) {
5705
5725
  }
5706
5726
 
5707
5727
  class SourceIndicationMsg {
5708
- constructor(seqNum, numTotalSources, numLiveSources, sources) {
5728
+ constructor(seqNum, numTotalSources, numLiveSources, sources, videoContentHint) {
5709
5729
  this.seqNum = seqNum;
5710
5730
  this.numTotalSources = numTotalSources;
5711
5731
  this.numLiveSources = numLiveSources;
5712
5732
  this.sources = sources;
5733
+ this.videoContentHint = videoContentHint;
5713
5734
  }
5714
5735
  sourcesToString() {
5715
5736
  return this.sources
@@ -5805,13 +5826,13 @@ class JmpSession extends EventEmitter$3 {
5805
5826
  this.logger.warn(`Retransmits for message expired: ${expiredJmpMsg}`);
5806
5827
  });
5807
5828
  }
5808
- updateSourceIndication(numTotalSources, numLiveSources, sources) {
5829
+ updateSourceIndication(numTotalSources, numLiveSources, sources, videoContentHint) {
5809
5830
  var _a;
5810
5831
  const filteredSources = sources.filter((source) => {
5811
5832
  var _a;
5812
5833
  return (_a = this.lastReceivedScr) === null || _a === void 0 ? void 0 : _a.requests.some((req) => req.ids.find((streamId) => compareStreamIds(streamId, source.id)));
5813
5834
  });
5814
- const sourceIndicationMsg = new SourceIndicationMsg(this.currSourceIndicationSeqNum++, numTotalSources, numLiveSources, filteredSources);
5835
+ const sourceIndicationMsg = new SourceIndicationMsg(this.currSourceIndicationSeqNum++, numTotalSources, numLiveSources, filteredSources, videoContentHint);
5815
5836
  const jmpMsg = new JmpMsg(this.mediaFamily, this.mediaContent, {
5816
5837
  msgType: JmpMsgType.SourceIndication,
5817
5838
  payload: sourceIndicationMsg,
@@ -9197,23 +9218,58 @@ class SsrcEgressStreamSignaler {
9197
9218
  this.streamIds = [];
9198
9219
  }
9199
9220
  signalStreams(simulcastEnabled, rtxEnabled, mLine) {
9221
+ var _a;
9200
9222
  mLine.rids = [];
9201
9223
  mLine.simulcast = undefined;
9202
- mLine.ssrcs = [];
9203
- mLine.ssrcGroups = [];
9204
9224
  mLine.extMaps = mLine.extMaps.filter((extMapLine) => !/^urn:ietf:params:rtp-hdrext:sdes:(?:mid|rtp-stream-id|repaired-rtp-stream-id)$/.test(extMapLine.uri));
9205
- if (this.streamIds.length === 0) {
9206
- const numStreams = simulcastEnabled ? 3 : 1;
9207
- [...Array(numStreams).keys()].forEach(() => {
9208
- const newStreamId = {
9209
- ssrc: generateSsrc(),
9210
- };
9211
- if (rtxEnabled) {
9212
- newStreamId.rtxSsrc = generateSsrc();
9225
+ const numStreams = simulcastEnabled ? 3 : 1;
9226
+ if (!this.streamIds.length) {
9227
+ if (mLine.ssrcs.length) {
9228
+ const ssrcs = [...new Set(mLine.ssrcs.map((ssrcLine) => ssrcLine.ssrcId))];
9229
+ mLine.ssrcGroups.forEach((sg) => {
9230
+ if (!sg.ssrcs.every((ssrc) => ssrcs.includes(ssrc))) {
9231
+ throw new Error('SSRC present in SSRC groups is missing from SSRC lines');
9232
+ }
9233
+ });
9234
+ const rtxSsrcGroups = mLine.ssrcGroups.filter((sg) => sg.semantics === 'FID');
9235
+ if (rtxSsrcGroups.length && rtxSsrcGroups.length !== numStreams) {
9236
+ throw new Error(`Expect ${numStreams} RTX SSRC groups, got ${rtxSsrcGroups.length}`);
9213
9237
  }
9214
- this.streamIds.push(newStreamId);
9215
- });
9238
+ rtxSsrcGroups.forEach((sg) => {
9239
+ this.streamIds.push({
9240
+ ssrc: sg.ssrcs[0],
9241
+ rtxSsrc: sg.ssrcs[1],
9242
+ });
9243
+ });
9244
+ const simulcastSsrcs = (_a = mLine.ssrcGroups.find((sg) => sg.semantics === 'SIM')) === null || _a === void 0 ? void 0 : _a.ssrcs;
9245
+ if (simulcastSsrcs) {
9246
+ if (simulcastSsrcs.length !== numStreams ||
9247
+ !this.streamIds.every((streamId) => simulcastSsrcs.includes(streamId.ssrc))) {
9248
+ throw new Error('SSRCs in simulcast SSRC group do not match primary SSRCs in RTX SSRC groups');
9249
+ }
9250
+ this.streamIds.sort((a, b) => simulcastSsrcs.indexOf(a.ssrc) - simulcastSsrcs.indexOf(b.ssrc));
9251
+ }
9252
+ else if (rtxSsrcGroups.length > 1) {
9253
+ throw new Error('Multiple RTX SSRC groups but no simulcast SSRC group found');
9254
+ }
9255
+ if (!this.streamIds.length) {
9256
+ this.streamIds.push({ ssrc: ssrcs[0] });
9257
+ }
9258
+ }
9259
+ else {
9260
+ [...Array(numStreams).keys()].forEach(() => {
9261
+ const newStreamId = {
9262
+ ssrc: generateSsrc(),
9263
+ };
9264
+ if (rtxEnabled) {
9265
+ newStreamId.rtxSsrc = generateSsrc();
9266
+ }
9267
+ this.streamIds.push(newStreamId);
9268
+ });
9269
+ }
9216
9270
  }
9271
+ mLine.ssrcs = [];
9272
+ mLine.ssrcGroups = [];
9217
9273
  this.streamIds.forEach((streamId) => {
9218
9274
  const rtpSsrc = streamId.ssrc;
9219
9275
  mLine.addLine(new SsrcLine(rtpSsrc, 'cname', `${rtpSsrc}-cname`));
@@ -9681,6 +9737,15 @@ function toMediaStreamTrackKind(mediaType) {
9681
9737
  ? MediaStreamTrackKind.Video
9682
9738
  : MediaStreamTrackKind.Audio;
9683
9739
  }
9740
+ function webRtcVideoContentHintToJmpVideoContentHint(hint) {
9741
+ if (hint === 'motion') {
9742
+ return 'motion';
9743
+ }
9744
+ if (hint === 'detail') {
9745
+ return 'sharpness';
9746
+ }
9747
+ return undefined;
9748
+ }
9684
9749
  function toMediaFamily(kind) {
9685
9750
  if (kind === MediaStreamTrackKind.Video) {
9686
9751
  return MediaFamily.Video;
@@ -9929,24 +9994,8 @@ class MultistreamConnection extends EventEmitter {
9929
9994
  const dataChannel = this.pc.createDataChannel('datachannel', {});
9930
9995
  dataChannel.onopen = (e) => {
9931
9996
  logger.info('DataChannel opened: ', e);
9932
- this.sendTransceivers.forEach((transceiver, mediaType) => {
9933
- const track = transceiver.publishedTrack;
9934
- if (track) {
9935
- if (getMediaFamily(mediaType) === MediaFamily.Audio) {
9936
- this.sendSourceIndication(mediaType, +!track.muted);
9937
- }
9938
- else {
9939
- const signaler = this.streamSignalerManager.getEgressStreamSignalerOrThrow(transceiver.mid);
9940
- const state = track.muted ? 'avatar' : 'live';
9941
- const sources = signaler
9942
- .getSenderIds()
9943
- .map((id) => ({ id, state, csi: transceiver.csi }));
9944
- this.sendSourceIndication(mediaType, +!track.muted, sources);
9945
- }
9946
- }
9947
- else {
9948
- this.sendSourceIndication(mediaType, 0);
9949
- }
9997
+ [...this.sendTransceivers.keys()].forEach((mediaType) => {
9998
+ this.maybeSendSourceIndication(mediaType);
9950
9999
  });
9951
10000
  logger.info(`Flushing pending JMP task queue`);
9952
10001
  this.pendingJmpTasks.forEach((t) => t());
@@ -9995,11 +10044,31 @@ class MultistreamConnection extends EventEmitter {
9995
10044
  });
9996
10045
  this.pc.close();
9997
10046
  }
9998
- sendSourceIndication(mediaType, numLiveSources, sources = []) {
10047
+ maybeSendSourceIndication(mediaType) {
10048
+ var _a;
10049
+ const transceiver = this.getSendTransceiverOrThrow(mediaType);
10050
+ const numLiveSources = ((_a = transceiver.publishedTrack) === null || _a === void 0 ? void 0 : _a.muted) === false ? 1 : 0;
10051
+ if (getMediaFamily(mediaType) === MediaFamily.Video) {
10052
+ const sources = this.getVideoSources(mediaType);
10053
+ if (sources === null) {
10054
+ return;
10055
+ }
10056
+ let webRtcVideoContentHint;
10057
+ if (transceiver.publishedTrack instanceof LocalDisplayTrack) {
10058
+ webRtcVideoContentHint = transceiver.publishedTrack.videoContentHint;
10059
+ }
10060
+ this.sendSourceIndication(mediaType, numLiveSources, sources, webRtcVideoContentHintToJmpVideoContentHint(webRtcVideoContentHint));
10061
+ }
10062
+ else {
10063
+ this.sendSourceIndication(mediaType, numLiveSources);
10064
+ }
10065
+ }
10066
+ sendSourceIndication(mediaType, numLiveSources, sources = [], videoContentHint = undefined) {
9999
10067
  var _a;
10000
10068
  const task = () => {
10001
10069
  var _a;
10002
- (_a = this.jmpSessions.get(mediaType)) === null || _a === void 0 ? void 0 : _a.updateSourceIndication(1, numLiveSources, sources);
10070
+ (_a = this.jmpSessions
10071
+ .get(mediaType)) === null || _a === void 0 ? void 0 : _a.updateSourceIndication(1, numLiveSources, sources, videoContentHint);
10003
10072
  };
10004
10073
  if (((_a = this.dataChannel) === null || _a === void 0 ? void 0 : _a.readyState) === 'open') {
10005
10074
  task();
@@ -10044,24 +10113,9 @@ class MultistreamConnection extends EventEmitter {
10044
10113
  });
10045
10114
  }
10046
10115
  addTrackListeners(mediaType, track) {
10047
- const onTrackResolutionChange = () => {
10048
- const sources = this.getVideoSources(mediaType);
10049
- if (sources != null) {
10050
- this.sendSourceIndication(mediaType, 1, sources);
10051
- }
10052
- };
10116
+ const onTrackResolutionChange = () => this.maybeSendSourceIndication(mediaType);
10053
10117
  track.on(LocalTrack.Events.TrackConstraintsChange, onTrackResolutionChange);
10054
- const onTrackMute = (event) => {
10055
- if (getMediaFamily(mediaType) === MediaFamily.Audio) {
10056
- this.sendSourceIndication(mediaType, +!event.trackState.muted);
10057
- }
10058
- else {
10059
- const sources = this.getVideoSources(mediaType);
10060
- if (sources != null) {
10061
- this.sendSourceIndication(mediaType, +!event.trackState.muted, sources);
10062
- }
10063
- }
10064
- };
10118
+ const onTrackMute = () => this.maybeSendSourceIndication(mediaType);
10065
10119
  track.on(LocalTrack.Events.Muted, onTrackMute);
10066
10120
  const onTrackPublish = (event) => {
10067
10121
  if (!event.isPublished) {
@@ -10069,14 +10123,8 @@ class MultistreamConnection extends EventEmitter {
10069
10123
  track.off(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);
10070
10124
  track.off(LocalTrack.Events.TrackConstraintsChange, onTrackResolutionChange);
10071
10125
  }
10072
- if (getMediaFamily(mediaType) === MediaFamily.Audio) {
10073
- this.sendSourceIndication(mediaType, +event.isPublished);
10074
- }
10075
- else {
10076
- const sources = this.getVideoSources(mediaType);
10077
- if (sources != null) {
10078
- this.sendSourceIndication(mediaType, +event.isPublished, sources);
10079
- }
10126
+ if (!track.muted) {
10127
+ this.maybeSendSourceIndication(mediaType);
10080
10128
  }
10081
10129
  };
10082
10130
  track.on(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);