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

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,
@@ -9185,7 +9206,9 @@ class SsrcIngressStreamSignaler {
9185
9206
  mLine.addLine(new SsrcLine(this.ssrc, 'cname', `${this.ssrc}-cname`));
9186
9207
  mLine.addLine(new SsrcLine(this.ssrc, 'msid', '-', '1'));
9187
9208
  if (hasCodec('rtx', mLine)) {
9188
- this.rtxSsrc = generateSsrc();
9209
+ if (!this.rtxSsrc) {
9210
+ this.rtxSsrc = generateSsrc();
9211
+ }
9189
9212
  mLine.addLine(new SsrcLine(this.rtxSsrc, 'cname', `${this.ssrc}-cname`));
9190
9213
  mLine.addLine(new SsrcLine(this.rtxSsrc, 'msid', '-', '1'));
9191
9214
  mLine.addLine(new SsrcGroupLine('FID', [this.ssrc, this.rtxSsrc]));
@@ -9197,23 +9220,58 @@ class SsrcEgressStreamSignaler {
9197
9220
  this.streamIds = [];
9198
9221
  }
9199
9222
  signalStreams(simulcastEnabled, rtxEnabled, mLine) {
9223
+ var _a;
9200
9224
  mLine.rids = [];
9201
9225
  mLine.simulcast = undefined;
9202
- mLine.ssrcs = [];
9203
- mLine.ssrcGroups = [];
9204
9226
  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();
9227
+ const numStreams = simulcastEnabled ? 3 : 1;
9228
+ if (!this.streamIds.length) {
9229
+ if (mLine.ssrcs.length) {
9230
+ const ssrcs = [...new Set(mLine.ssrcs.map((ssrcLine) => ssrcLine.ssrcId))];
9231
+ mLine.ssrcGroups.forEach((sg) => {
9232
+ if (!sg.ssrcs.every((ssrc) => ssrcs.includes(ssrc))) {
9233
+ throw new Error('SSRC present in SSRC groups is missing from SSRC lines');
9234
+ }
9235
+ });
9236
+ const rtxSsrcGroups = mLine.ssrcGroups.filter((sg) => sg.semantics === 'FID');
9237
+ if (rtxSsrcGroups.length && rtxSsrcGroups.length !== numStreams) {
9238
+ throw new Error(`Expect ${numStreams} RTX SSRC groups, got ${rtxSsrcGroups.length}`);
9213
9239
  }
9214
- this.streamIds.push(newStreamId);
9215
- });
9240
+ rtxSsrcGroups.forEach((sg) => {
9241
+ this.streamIds.push({
9242
+ ssrc: sg.ssrcs[0],
9243
+ rtxSsrc: sg.ssrcs[1],
9244
+ });
9245
+ });
9246
+ const simulcastSsrcs = (_a = mLine.ssrcGroups.find((sg) => sg.semantics === 'SIM')) === null || _a === void 0 ? void 0 : _a.ssrcs;
9247
+ if (simulcastSsrcs) {
9248
+ if (simulcastSsrcs.length !== numStreams ||
9249
+ !this.streamIds.every((streamId) => simulcastSsrcs.includes(streamId.ssrc))) {
9250
+ throw new Error('SSRCs in simulcast SSRC group do not match primary SSRCs in RTX SSRC groups');
9251
+ }
9252
+ this.streamIds.sort((a, b) => simulcastSsrcs.indexOf(a.ssrc) - simulcastSsrcs.indexOf(b.ssrc));
9253
+ }
9254
+ else if (rtxSsrcGroups.length > 1) {
9255
+ throw new Error('Multiple RTX SSRC groups but no simulcast SSRC group found');
9256
+ }
9257
+ if (!this.streamIds.length) {
9258
+ this.streamIds.push({ ssrc: ssrcs[0] });
9259
+ }
9260
+ }
9261
+ else {
9262
+ [...Array(numStreams).keys()].forEach(() => {
9263
+ const newStreamId = {
9264
+ ssrc: generateSsrc(),
9265
+ };
9266
+ if (rtxEnabled) {
9267
+ newStreamId.rtxSsrc = generateSsrc();
9268
+ }
9269
+ this.streamIds.push(newStreamId);
9270
+ });
9271
+ }
9216
9272
  }
9273
+ mLine.ssrcs = [];
9274
+ mLine.ssrcGroups = [];
9217
9275
  this.streamIds.forEach((streamId) => {
9218
9276
  const rtpSsrc = streamId.ssrc;
9219
9277
  mLine.addLine(new SsrcLine(rtpSsrc, 'cname', `${rtpSsrc}-cname`));
@@ -9681,6 +9739,15 @@ function toMediaStreamTrackKind(mediaType) {
9681
9739
  ? MediaStreamTrackKind.Video
9682
9740
  : MediaStreamTrackKind.Audio;
9683
9741
  }
9742
+ function webRtcVideoContentHintToJmpVideoContentHint(hint) {
9743
+ if (hint === 'motion') {
9744
+ return 'motion';
9745
+ }
9746
+ if (hint === 'detail') {
9747
+ return 'sharpness';
9748
+ }
9749
+ return undefined;
9750
+ }
9684
9751
  function toMediaFamily(kind) {
9685
9752
  if (kind === MediaStreamTrackKind.Video) {
9686
9753
  return MediaFamily.Video;
@@ -9929,24 +9996,8 @@ class MultistreamConnection extends EventEmitter {
9929
9996
  const dataChannel = this.pc.createDataChannel('datachannel', {});
9930
9997
  dataChannel.onopen = (e) => {
9931
9998
  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
- }
9999
+ [...this.sendTransceivers.keys()].forEach((mediaType) => {
10000
+ this.maybeSendSourceIndication(mediaType);
9950
10001
  });
9951
10002
  logger.info(`Flushing pending JMP task queue`);
9952
10003
  this.pendingJmpTasks.forEach((t) => t());
@@ -9995,11 +10046,31 @@ class MultistreamConnection extends EventEmitter {
9995
10046
  });
9996
10047
  this.pc.close();
9997
10048
  }
9998
- sendSourceIndication(mediaType, numLiveSources, sources = []) {
10049
+ maybeSendSourceIndication(mediaType) {
10050
+ var _a;
10051
+ const transceiver = this.getSendTransceiverOrThrow(mediaType);
10052
+ const numLiveSources = ((_a = transceiver.publishedTrack) === null || _a === void 0 ? void 0 : _a.muted) === false ? 1 : 0;
10053
+ if (getMediaFamily(mediaType) === MediaFamily.Video) {
10054
+ const sources = this.getVideoSources(mediaType);
10055
+ if (sources === null) {
10056
+ return;
10057
+ }
10058
+ let webRtcVideoContentHint;
10059
+ if (transceiver.publishedTrack instanceof LocalDisplayTrack) {
10060
+ webRtcVideoContentHint = transceiver.publishedTrack.videoContentHint;
10061
+ }
10062
+ this.sendSourceIndication(mediaType, numLiveSources, sources, webRtcVideoContentHintToJmpVideoContentHint(webRtcVideoContentHint));
10063
+ }
10064
+ else {
10065
+ this.sendSourceIndication(mediaType, numLiveSources);
10066
+ }
10067
+ }
10068
+ sendSourceIndication(mediaType, numLiveSources, sources = [], videoContentHint = undefined) {
9999
10069
  var _a;
10000
10070
  const task = () => {
10001
10071
  var _a;
10002
- (_a = this.jmpSessions.get(mediaType)) === null || _a === void 0 ? void 0 : _a.updateSourceIndication(1, numLiveSources, sources);
10072
+ (_a = this.jmpSessions
10073
+ .get(mediaType)) === null || _a === void 0 ? void 0 : _a.updateSourceIndication(1, numLiveSources, sources, videoContentHint);
10003
10074
  };
10004
10075
  if (((_a = this.dataChannel) === null || _a === void 0 ? void 0 : _a.readyState) === 'open') {
10005
10076
  task();
@@ -10044,24 +10115,9 @@ class MultistreamConnection extends EventEmitter {
10044
10115
  });
10045
10116
  }
10046
10117
  addTrackListeners(mediaType, track) {
10047
- const onTrackResolutionChange = () => {
10048
- const sources = this.getVideoSources(mediaType);
10049
- if (sources != null) {
10050
- this.sendSourceIndication(mediaType, 1, sources);
10051
- }
10052
- };
10118
+ const onTrackResolutionChange = () => this.maybeSendSourceIndication(mediaType);
10053
10119
  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
- };
10120
+ const onTrackMute = () => this.maybeSendSourceIndication(mediaType);
10065
10121
  track.on(LocalTrack.Events.Muted, onTrackMute);
10066
10122
  const onTrackPublish = (event) => {
10067
10123
  if (!event.isPublished) {
@@ -10069,14 +10125,8 @@ class MultistreamConnection extends EventEmitter {
10069
10125
  track.off(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);
10070
10126
  track.off(LocalTrack.Events.TrackConstraintsChange, onTrackResolutionChange);
10071
10127
  }
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
- }
10128
+ if (!track.muted) {
10129
+ this.maybeSendSourceIndication(mediaType);
10080
10130
  }
10081
10131
  };
10082
10132
  track.on(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);