@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/cjs/index.js CHANGED
@@ -547,12 +547,13 @@ function createMicrophoneTrack(constructor, constraints) {
547
547
  * Creates a display video track.
548
548
  *
549
549
  * @param constructor - Constructor for the local display track.
550
+ * @param videoContentHint - An optional parameters to give a hint for the content of the track.
550
551
  * @returns A Promise that resolves to a LocalDisplayTrack.
551
552
  */
552
- function createDisplayTrack(constructor) {
553
+ function createDisplayTrack(constructor, videoContentHint) {
553
554
  return __awaiter$1(this, void 0, void 0, function* () {
554
555
  const stream = yield getDisplayMedia({ video: true });
555
- return new constructor(stream);
556
+ return new constructor(stream, videoContentHint);
556
557
  });
557
558
  }
558
559
  /**
@@ -1358,6 +1359,25 @@ class LocalCameraTrack extends LocalTrack {
1358
1359
  * Represents a local track for a display source.
1359
1360
  */
1360
1361
  class LocalDisplayTrack extends LocalTrack {
1362
+ /**
1363
+ * Create a LocalDisplayTrack from the given values.
1364
+ *
1365
+ * @param stream - The MediaStream for this track.
1366
+ * @param videoContentHint - An optional content hint, describing the content of the track.
1367
+ */
1368
+ constructor(stream, videoContentHint) {
1369
+ super(stream);
1370
+ this._videoContentHint = videoContentHint;
1371
+ this.underlyingTrack.contentHint = videoContentHint || '';
1372
+ }
1373
+ /**
1374
+ * Get the VideoContentHint for this track.
1375
+ *
1376
+ * @returns The VideoContentHint for this track.
1377
+ */
1378
+ get videoContentHint() {
1379
+ return this._videoContentHint;
1380
+ }
1361
1381
  }
1362
1382
 
1363
1383
  /**
@@ -5709,11 +5729,12 @@ function compareStreamIds(id1, id2) {
5709
5729
  }
5710
5730
 
5711
5731
  class SourceIndicationMsg {
5712
- constructor(seqNum, numTotalSources, numLiveSources, sources) {
5732
+ constructor(seqNum, numTotalSources, numLiveSources, sources, videoContentHint) {
5713
5733
  this.seqNum = seqNum;
5714
5734
  this.numTotalSources = numTotalSources;
5715
5735
  this.numLiveSources = numLiveSources;
5716
5736
  this.sources = sources;
5737
+ this.videoContentHint = videoContentHint;
5717
5738
  }
5718
5739
  sourcesToString() {
5719
5740
  return this.sources
@@ -5809,13 +5830,13 @@ class JmpSession extends events$2.EventEmitter {
5809
5830
  this.logger.warn(`Retransmits for message expired: ${expiredJmpMsg}`);
5810
5831
  });
5811
5832
  }
5812
- updateSourceIndication(numTotalSources, numLiveSources, sources) {
5833
+ updateSourceIndication(numTotalSources, numLiveSources, sources, videoContentHint) {
5813
5834
  var _a;
5814
5835
  const filteredSources = sources.filter((source) => {
5815
5836
  var _a;
5816
5837
  return (_a = this.lastReceivedScr) === null || _a === void 0 ? void 0 : _a.requests.some((req) => req.ids.find((streamId) => compareStreamIds(streamId, source.id)));
5817
5838
  });
5818
- const sourceIndicationMsg = new SourceIndicationMsg(this.currSourceIndicationSeqNum++, numTotalSources, numLiveSources, filteredSources);
5839
+ const sourceIndicationMsg = new SourceIndicationMsg(this.currSourceIndicationSeqNum++, numTotalSources, numLiveSources, filteredSources, videoContentHint);
5819
5840
  const jmpMsg = new JmpMsg(this.mediaFamily, this.mediaContent, {
5820
5841
  msgType: JmpMsgType.SourceIndication,
5821
5842
  payload: sourceIndicationMsg,
@@ -9189,7 +9210,9 @@ class SsrcIngressStreamSignaler {
9189
9210
  mLine.addLine(new SsrcLine(this.ssrc, 'cname', `${this.ssrc}-cname`));
9190
9211
  mLine.addLine(new SsrcLine(this.ssrc, 'msid', '-', '1'));
9191
9212
  if (hasCodec('rtx', mLine)) {
9192
- this.rtxSsrc = generateSsrc();
9213
+ if (!this.rtxSsrc) {
9214
+ this.rtxSsrc = generateSsrc();
9215
+ }
9193
9216
  mLine.addLine(new SsrcLine(this.rtxSsrc, 'cname', `${this.ssrc}-cname`));
9194
9217
  mLine.addLine(new SsrcLine(this.rtxSsrc, 'msid', '-', '1'));
9195
9218
  mLine.addLine(new SsrcGroupLine('FID', [this.ssrc, this.rtxSsrc]));
@@ -9201,23 +9224,58 @@ class SsrcEgressStreamSignaler {
9201
9224
  this.streamIds = [];
9202
9225
  }
9203
9226
  signalStreams(simulcastEnabled, rtxEnabled, mLine) {
9227
+ var _a;
9204
9228
  mLine.rids = [];
9205
9229
  mLine.simulcast = undefined;
9206
- mLine.ssrcs = [];
9207
- mLine.ssrcGroups = [];
9208
9230
  mLine.extMaps = mLine.extMaps.filter((extMapLine) => !/^urn:ietf:params:rtp-hdrext:sdes:(?:mid|rtp-stream-id|repaired-rtp-stream-id)$/.test(extMapLine.uri));
9209
- if (this.streamIds.length === 0) {
9210
- const numStreams = simulcastEnabled ? 3 : 1;
9211
- [...Array(numStreams).keys()].forEach(() => {
9212
- const newStreamId = {
9213
- ssrc: generateSsrc(),
9214
- };
9215
- if (rtxEnabled) {
9216
- newStreamId.rtxSsrc = generateSsrc();
9231
+ const numStreams = simulcastEnabled ? 3 : 1;
9232
+ if (!this.streamIds.length) {
9233
+ if (mLine.ssrcs.length) {
9234
+ const ssrcs = [...new Set(mLine.ssrcs.map((ssrcLine) => ssrcLine.ssrcId))];
9235
+ mLine.ssrcGroups.forEach((sg) => {
9236
+ if (!sg.ssrcs.every((ssrc) => ssrcs.includes(ssrc))) {
9237
+ throw new Error('SSRC present in SSRC groups is missing from SSRC lines');
9238
+ }
9239
+ });
9240
+ const rtxSsrcGroups = mLine.ssrcGroups.filter((sg) => sg.semantics === 'FID');
9241
+ if (rtxSsrcGroups.length && rtxSsrcGroups.length !== numStreams) {
9242
+ throw new Error(`Expect ${numStreams} RTX SSRC groups, got ${rtxSsrcGroups.length}`);
9217
9243
  }
9218
- this.streamIds.push(newStreamId);
9219
- });
9244
+ rtxSsrcGroups.forEach((sg) => {
9245
+ this.streamIds.push({
9246
+ ssrc: sg.ssrcs[0],
9247
+ rtxSsrc: sg.ssrcs[1],
9248
+ });
9249
+ });
9250
+ const simulcastSsrcs = (_a = mLine.ssrcGroups.find((sg) => sg.semantics === 'SIM')) === null || _a === void 0 ? void 0 : _a.ssrcs;
9251
+ if (simulcastSsrcs) {
9252
+ if (simulcastSsrcs.length !== numStreams ||
9253
+ !this.streamIds.every((streamId) => simulcastSsrcs.includes(streamId.ssrc))) {
9254
+ throw new Error('SSRCs in simulcast SSRC group do not match primary SSRCs in RTX SSRC groups');
9255
+ }
9256
+ this.streamIds.sort((a, b) => simulcastSsrcs.indexOf(a.ssrc) - simulcastSsrcs.indexOf(b.ssrc));
9257
+ }
9258
+ else if (rtxSsrcGroups.length > 1) {
9259
+ throw new Error('Multiple RTX SSRC groups but no simulcast SSRC group found');
9260
+ }
9261
+ if (!this.streamIds.length) {
9262
+ this.streamIds.push({ ssrc: ssrcs[0] });
9263
+ }
9264
+ }
9265
+ else {
9266
+ [...Array(numStreams).keys()].forEach(() => {
9267
+ const newStreamId = {
9268
+ ssrc: generateSsrc(),
9269
+ };
9270
+ if (rtxEnabled) {
9271
+ newStreamId.rtxSsrc = generateSsrc();
9272
+ }
9273
+ this.streamIds.push(newStreamId);
9274
+ });
9275
+ }
9220
9276
  }
9277
+ mLine.ssrcs = [];
9278
+ mLine.ssrcGroups = [];
9221
9279
  this.streamIds.forEach((streamId) => {
9222
9280
  const rtpSsrc = streamId.ssrc;
9223
9281
  mLine.addLine(new SsrcLine(rtpSsrc, 'cname', `${rtpSsrc}-cname`));
@@ -9685,6 +9743,15 @@ function toMediaStreamTrackKind(mediaType) {
9685
9743
  ? exports.MediaStreamTrackKind.Video
9686
9744
  : exports.MediaStreamTrackKind.Audio;
9687
9745
  }
9746
+ function webRtcVideoContentHintToJmpVideoContentHint(hint) {
9747
+ if (hint === 'motion') {
9748
+ return 'motion';
9749
+ }
9750
+ if (hint === 'detail') {
9751
+ return 'sharpness';
9752
+ }
9753
+ return undefined;
9754
+ }
9688
9755
  function toMediaFamily(kind) {
9689
9756
  if (kind === exports.MediaStreamTrackKind.Video) {
9690
9757
  return MediaFamily.Video;
@@ -9933,24 +10000,8 @@ class MultistreamConnection extends EventEmitter {
9933
10000
  const dataChannel = this.pc.createDataChannel('datachannel', {});
9934
10001
  dataChannel.onopen = (e) => {
9935
10002
  logger.info('DataChannel opened: ', e);
9936
- this.sendTransceivers.forEach((transceiver, mediaType) => {
9937
- const track = transceiver.publishedTrack;
9938
- if (track) {
9939
- if (getMediaFamily(mediaType) === MediaFamily.Audio) {
9940
- this.sendSourceIndication(mediaType, +!track.muted);
9941
- }
9942
- else {
9943
- const signaler = this.streamSignalerManager.getEgressStreamSignalerOrThrow(transceiver.mid);
9944
- const state = track.muted ? 'avatar' : 'live';
9945
- const sources = signaler
9946
- .getSenderIds()
9947
- .map((id) => ({ id, state, csi: transceiver.csi }));
9948
- this.sendSourceIndication(mediaType, +!track.muted, sources);
9949
- }
9950
- }
9951
- else {
9952
- this.sendSourceIndication(mediaType, 0);
9953
- }
10003
+ [...this.sendTransceivers.keys()].forEach((mediaType) => {
10004
+ this.maybeSendSourceIndication(mediaType);
9954
10005
  });
9955
10006
  logger.info(`Flushing pending JMP task queue`);
9956
10007
  this.pendingJmpTasks.forEach((t) => t());
@@ -9999,11 +10050,31 @@ class MultistreamConnection extends EventEmitter {
9999
10050
  });
10000
10051
  this.pc.close();
10001
10052
  }
10002
- sendSourceIndication(mediaType, numLiveSources, sources = []) {
10053
+ maybeSendSourceIndication(mediaType) {
10054
+ var _a;
10055
+ const transceiver = this.getSendTransceiverOrThrow(mediaType);
10056
+ const numLiveSources = ((_a = transceiver.publishedTrack) === null || _a === void 0 ? void 0 : _a.muted) === false ? 1 : 0;
10057
+ if (getMediaFamily(mediaType) === MediaFamily.Video) {
10058
+ const sources = this.getVideoSources(mediaType);
10059
+ if (sources === null) {
10060
+ return;
10061
+ }
10062
+ let webRtcVideoContentHint;
10063
+ if (transceiver.publishedTrack instanceof LocalDisplayTrack) {
10064
+ webRtcVideoContentHint = transceiver.publishedTrack.videoContentHint;
10065
+ }
10066
+ this.sendSourceIndication(mediaType, numLiveSources, sources, webRtcVideoContentHintToJmpVideoContentHint(webRtcVideoContentHint));
10067
+ }
10068
+ else {
10069
+ this.sendSourceIndication(mediaType, numLiveSources);
10070
+ }
10071
+ }
10072
+ sendSourceIndication(mediaType, numLiveSources, sources = [], videoContentHint = undefined) {
10003
10073
  var _a;
10004
10074
  const task = () => {
10005
10075
  var _a;
10006
- (_a = this.jmpSessions.get(mediaType)) === null || _a === void 0 ? void 0 : _a.updateSourceIndication(1, numLiveSources, sources);
10076
+ (_a = this.jmpSessions
10077
+ .get(mediaType)) === null || _a === void 0 ? void 0 : _a.updateSourceIndication(1, numLiveSources, sources, videoContentHint);
10007
10078
  };
10008
10079
  if (((_a = this.dataChannel) === null || _a === void 0 ? void 0 : _a.readyState) === 'open') {
10009
10080
  task();
@@ -10048,24 +10119,9 @@ class MultistreamConnection extends EventEmitter {
10048
10119
  });
10049
10120
  }
10050
10121
  addTrackListeners(mediaType, track) {
10051
- const onTrackResolutionChange = () => {
10052
- const sources = this.getVideoSources(mediaType);
10053
- if (sources != null) {
10054
- this.sendSourceIndication(mediaType, 1, sources);
10055
- }
10056
- };
10122
+ const onTrackResolutionChange = () => this.maybeSendSourceIndication(mediaType);
10057
10123
  track.on(LocalTrack.Events.TrackConstraintsChange, onTrackResolutionChange);
10058
- const onTrackMute = (event) => {
10059
- if (getMediaFamily(mediaType) === MediaFamily.Audio) {
10060
- this.sendSourceIndication(mediaType, +!event.trackState.muted);
10061
- }
10062
- else {
10063
- const sources = this.getVideoSources(mediaType);
10064
- if (sources != null) {
10065
- this.sendSourceIndication(mediaType, +!event.trackState.muted, sources);
10066
- }
10067
- }
10068
- };
10124
+ const onTrackMute = () => this.maybeSendSourceIndication(mediaType);
10069
10125
  track.on(LocalTrack.Events.Muted, onTrackMute);
10070
10126
  const onTrackPublish = (event) => {
10071
10127
  if (!event.isPublished) {
@@ -10073,14 +10129,8 @@ class MultistreamConnection extends EventEmitter {
10073
10129
  track.off(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);
10074
10130
  track.off(LocalTrack.Events.TrackConstraintsChange, onTrackResolutionChange);
10075
10131
  }
10076
- if (getMediaFamily(mediaType) === MediaFamily.Audio) {
10077
- this.sendSourceIndication(mediaType, +event.isPublished);
10078
- }
10079
- else {
10080
- const sources = this.getVideoSources(mediaType);
10081
- if (sources != null) {
10082
- this.sendSourceIndication(mediaType, +event.isPublished, sources);
10083
- }
10132
+ if (!track.muted) {
10133
+ this.maybeSendSourceIndication(mediaType);
10084
10134
  }
10085
10135
  };
10086
10136
  track.on(LocalTrack.Events.PublishedStateUpdate, onTrackPublish);