@stream-io/video-client 0.3.29 → 0.3.30

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/index.browser.es.js +379 -116
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +379 -114
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +379 -116
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/Call.d.ts +14 -10
  9. package/dist/src/devices/CameraManager.d.ts +0 -1
  10. package/dist/src/devices/InputMediaDeviceManager.d.ts +18 -15
  11. package/dist/src/devices/InputMediaDeviceManagerState.d.ts +22 -6
  12. package/dist/src/devices/MicrophoneManager.d.ts +0 -1
  13. package/dist/src/devices/ScreenShareManager.d.ts +39 -0
  14. package/dist/src/devices/ScreenShareState.d.ts +36 -0
  15. package/dist/src/devices/__tests__/ScreenShareManager.test.d.ts +1 -0
  16. package/dist/src/devices/__tests__/mocks.d.ts +3 -7
  17. package/dist/src/devices/index.d.ts +2 -0
  18. package/dist/src/helpers/DynascaleManager.d.ts +3 -2
  19. package/dist/src/helpers/__tests__/hq-audio-sdp.d.ts +1 -0
  20. package/dist/src/helpers/sdp-munging.d.ts +8 -0
  21. package/dist/src/rtc/Publisher.d.ts +7 -4
  22. package/dist/src/rtc/helpers/tracks.d.ts +2 -1
  23. package/dist/src/rtc/videoLayers.d.ts +2 -1
  24. package/dist/src/types.d.ts +20 -0
  25. package/dist/version.d.ts +1 -1
  26. package/package.json +1 -1
  27. package/src/Call.ts +56 -12
  28. package/src/devices/CameraManager.ts +3 -4
  29. package/src/devices/InputMediaDeviceManager.ts +60 -45
  30. package/src/devices/InputMediaDeviceManagerState.ts +34 -14
  31. package/src/devices/MicrophoneManager.ts +3 -4
  32. package/src/devices/ScreenShareManager.ts +85 -0
  33. package/src/devices/ScreenShareState.ts +63 -0
  34. package/src/devices/__tests__/InputMediaDeviceManager.test.ts +16 -1
  35. package/src/devices/__tests__/ScreenShareManager.test.ts +119 -0
  36. package/src/devices/__tests__/mocks.ts +38 -1
  37. package/src/devices/devices.ts +10 -1
  38. package/src/devices/index.ts +2 -0
  39. package/src/helpers/DynascaleManager.ts +18 -3
  40. package/src/helpers/__tests__/DynascaleManager.test.ts +36 -1
  41. package/src/helpers/__tests__/hq-audio-sdp.ts +332 -0
  42. package/src/helpers/__tests__/sdp-munging.test.ts +13 -1
  43. package/src/helpers/sdp-munging.ts +49 -0
  44. package/src/rtc/Publisher.ts +87 -48
  45. package/src/rtc/Subscriber.ts +4 -1
  46. package/src/rtc/helpers/tracks.ts +16 -6
  47. package/src/rtc/videoLayers.ts +4 -2
  48. package/src/types.ts +27 -0
package/dist/index.cjs.js CHANGED
@@ -6219,7 +6219,8 @@ const withSimulcastConstraints = (settings, optimalVideoLayers) => {
6219
6219
  const ridMapping = ['q', 'h', 'f'];
6220
6220
  return layers.map((layer, index) => (Object.assign(Object.assign({}, layer), { rid: ridMapping[index] })));
6221
6221
  };
6222
- const findOptimalScreenSharingLayers = (videoTrack) => {
6222
+ const findOptimalScreenSharingLayers = (videoTrack, preferences) => {
6223
+ var _a, _b;
6223
6224
  const settings = videoTrack.getSettings();
6224
6225
  return [
6225
6226
  {
@@ -6227,9 +6228,9 @@ const findOptimalScreenSharingLayers = (videoTrack) => {
6227
6228
  rid: 'q',
6228
6229
  width: settings.width || 0,
6229
6230
  height: settings.height || 0,
6230
- maxBitrate: 3000000,
6231
6231
  scaleResolutionDownBy: 1,
6232
- maxFramerate: 30,
6232
+ maxBitrate: (_a = preferences === null || preferences === void 0 ? void 0 : preferences.maxBitrate) !== null && _a !== void 0 ? _a : 3000000,
6233
+ maxFramerate: (_b = preferences === null || preferences === void 0 ? void 0 : preferences.maxFramerate) !== null && _b !== void 0 ? _b : 30,
6233
6234
  },
6234
6235
  ];
6235
6236
  };
@@ -6238,12 +6239,17 @@ const trackTypeToParticipantStreamKey = (trackType) => {
6238
6239
  switch (trackType) {
6239
6240
  case TrackType.SCREEN_SHARE:
6240
6241
  return 'screenShareStream';
6242
+ case TrackType.SCREEN_SHARE_AUDIO:
6243
+ return 'screenShareAudioStream';
6241
6244
  case TrackType.VIDEO:
6242
6245
  return 'videoStream';
6243
6246
  case TrackType.AUDIO:
6244
6247
  return 'audioStream';
6248
+ case TrackType.UNSPECIFIED:
6249
+ throw new Error('Track type is unspecified');
6245
6250
  default:
6246
- throw new Error(`Unknown track type: ${trackType}`);
6251
+ const exhaustiveTrackTypeCheck = trackType;
6252
+ throw new Error(`Unknown track type: ${exhaustiveTrackTypeCheck}`);
6247
6253
  }
6248
6254
  };
6249
6255
  const trackTypeToDeviceIdKey = (trackType) => {
@@ -6253,9 +6259,12 @@ const trackTypeToDeviceIdKey = (trackType) => {
6253
6259
  case TrackType.VIDEO:
6254
6260
  return 'videoDeviceId';
6255
6261
  case TrackType.SCREEN_SHARE:
6262
+ case TrackType.SCREEN_SHARE_AUDIO:
6263
+ case TrackType.UNSPECIFIED:
6256
6264
  return undefined;
6257
6265
  default:
6258
- throw new Error(`Unknown track type: ${trackType}`);
6266
+ const exhaustiveTrackTypeCheck = trackType;
6267
+ throw new Error(`Unknown track type: ${exhaustiveTrackTypeCheck}`);
6259
6268
  }
6260
6269
  };
6261
6270
  const muteTypeToTrackType = (muteType) => {
@@ -6266,8 +6275,11 @@ const muteTypeToTrackType = (muteType) => {
6266
6275
  return TrackType.VIDEO;
6267
6276
  case 'screenshare':
6268
6277
  return TrackType.SCREEN_SHARE;
6278
+ case 'screenshare_audio':
6279
+ return TrackType.SCREEN_SHARE_AUDIO;
6269
6280
  default:
6270
- throw new Error(`Unknown mute type: ${muteType}`);
6281
+ const exhaustiveMuteTypeCheck = muteType;
6282
+ throw new Error(`Unknown mute type: ${exhaustiveMuteTypeCheck}`);
6271
6283
  }
6272
6284
  };
6273
6285
 
@@ -6383,6 +6395,41 @@ const toggleDtx = (sdp, enable) => {
6383
6395
  }
6384
6396
  return sdp;
6385
6397
  };
6398
+ /**
6399
+ * Enables high-quality audio through SDP munging for the given trackMid.
6400
+ *
6401
+ * @param sdp the SDP to munge.
6402
+ * @param trackMid the trackMid.
6403
+ * @param maxBitrate the max bitrate to set.
6404
+ */
6405
+ const enableHighQualityAudio = (sdp, trackMid, maxBitrate = 510000) => {
6406
+ maxBitrate = Math.max(Math.min(maxBitrate, 510000), 96000);
6407
+ const parsedSdp = SDP__namespace.parse(sdp);
6408
+ const audioMedia = parsedSdp.media.find((m) => m.type === 'audio' && String(m.mid) === trackMid);
6409
+ if (!audioMedia)
6410
+ return sdp;
6411
+ const opusRtp = audioMedia.rtp.find((r) => r.codec === 'opus');
6412
+ if (!opusRtp)
6413
+ return sdp;
6414
+ const opusFmtp = audioMedia.fmtp.find((f) => f.payload === opusRtp.payload);
6415
+ if (!opusFmtp)
6416
+ return sdp;
6417
+ // enable stereo, if not already enabled
6418
+ if (opusFmtp.config.match(/stereo=(\d)/)) {
6419
+ opusFmtp.config = opusFmtp.config.replace(/stereo=(\d)/, 'stereo=1');
6420
+ }
6421
+ else {
6422
+ opusFmtp.config = `${opusFmtp.config};stereo=1`;
6423
+ }
6424
+ // set maxaveragebitrate, to the given value
6425
+ if (opusFmtp.config.match(/maxaveragebitrate=(\d*)/)) {
6426
+ opusFmtp.config = opusFmtp.config.replace(/maxaveragebitrate=(\d*)/, `maxaveragebitrate=${maxBitrate}`);
6427
+ }
6428
+ else {
6429
+ opusFmtp.config = `${opusFmtp.config};maxaveragebitrate=${maxBitrate}`;
6430
+ }
6431
+ return SDP__namespace.write(parsedSdp);
6432
+ };
6386
6433
 
6387
6434
  const logger$3 = getLogger(['Publisher']);
6388
6435
  /**
@@ -6409,6 +6456,7 @@ class Publisher {
6409
6456
  [TrackType.SCREEN_SHARE_AUDIO]: undefined,
6410
6457
  [TrackType.UNSPECIFIED]: undefined,
6411
6458
  };
6459
+ this.publishOptionsPerTrackType = new Map();
6412
6460
  /**
6413
6461
  * An array maintaining the order how transceivers were added to the peer connection.
6414
6462
  * This is needed because some browsers (Firefox) don't reliably report
@@ -6421,7 +6469,7 @@ class Publisher {
6421
6469
  [TrackType.AUDIO]: 'audio',
6422
6470
  [TrackType.VIDEO]: 'video',
6423
6471
  [TrackType.SCREEN_SHARE]: 'video',
6424
- [TrackType.SCREEN_SHARE_AUDIO]: undefined,
6472
+ [TrackType.SCREEN_SHARE_AUDIO]: 'audio',
6425
6473
  [TrackType.UNSPECIFIED]: undefined,
6426
6474
  };
6427
6475
  this.trackLayersCache = {
@@ -6466,10 +6514,11 @@ class Publisher {
6466
6514
  *
6467
6515
  * Consecutive calls to this method will replace the stream.
6468
6516
  * The previous stream will be stopped.
6469
- * @param mediaStream
6470
- * @param track
6471
- * @param trackType
6472
- * @param opts
6517
+ *
6518
+ * @param mediaStream the media stream to publish.
6519
+ * @param track the track to publish.
6520
+ * @param trackType the track type to publish.
6521
+ * @param opts the optional publish options to use.
6473
6522
  */
6474
6523
  this.publishStream = (mediaStream, track, trackType, opts = {}) => __awaiter(this, void 0, void 0, function* () {
6475
6524
  var _a;
@@ -6499,7 +6548,9 @@ class Publisher {
6499
6548
  const targetResolution = settings === null || settings === void 0 ? void 0 : settings.video.target_resolution;
6500
6549
  const videoEncodings = trackType === TrackType.VIDEO
6501
6550
  ? findOptimalVideoLayers(track, targetResolution)
6502
- : undefined;
6551
+ : trackType === TrackType.SCREEN_SHARE
6552
+ ? findOptimalScreenSharingLayers(track, opts.screenShareSettings)
6553
+ : undefined;
6503
6554
  let preferredCodec = opts.preferredCodec;
6504
6555
  if (!preferredCodec && trackType === TrackType.VIDEO) {
6505
6556
  const isRNAndroid = isReactNative() && ((_a = getOSInfo()) === null || _a === void 0 ? void 0 : _a.name.toLowerCase()) === 'android';
@@ -6525,6 +6576,7 @@ class Publisher {
6525
6576
  logger$3('debug', `Added ${TrackType[trackType]} transceiver`);
6526
6577
  this.transceiverInitOrder.push(trackType);
6527
6578
  this.transceiverRegistry[trackType] = transceiver;
6579
+ this.publishOptionsPerTrackType.set(trackType, opts);
6528
6580
  if ('setCodecPreferences' in transceiver && codecPreferences) {
6529
6581
  logger$3('info', `Setting ${TrackType[trackType]} codec preferences`, codecPreferences);
6530
6582
  transceiver.setCodecPreferences(codecPreferences);
@@ -6734,10 +6786,19 @@ class Publisher {
6734
6786
  * @param options the optional offer options to use.
6735
6787
  */
6736
6788
  this.negotiate = (options) => __awaiter(this, void 0, void 0, function* () {
6737
- var _d;
6789
+ var _d, _e;
6738
6790
  this.isIceRestarting = (_d = options === null || options === void 0 ? void 0 : options.iceRestart) !== null && _d !== void 0 ? _d : false;
6739
6791
  const offer = yield this.pc.createOffer(options);
6740
- offer.sdp = this.mungeCodecs(offer.sdp);
6792
+ let sdp = this.mungeCodecs(offer.sdp);
6793
+ if (sdp && this.isPublishing(TrackType.SCREEN_SHARE_AUDIO)) {
6794
+ const transceiver = this.transceiverRegistry[TrackType.SCREEN_SHARE_AUDIO];
6795
+ if (transceiver && transceiver.sender.track) {
6796
+ const mid = (_e = transceiver.mid) !== null && _e !== void 0 ? _e : this.extractMid(sdp, transceiver.sender.track, TrackType.SCREEN_SHARE_AUDIO);
6797
+ sdp = enableHighQualityAudio(sdp, mid);
6798
+ }
6799
+ }
6800
+ // set the munged SDP back to the offer
6801
+ offer.sdp = sdp;
6741
6802
  const trackInfos = this.getCurrentTrackInfos(offer.sdp);
6742
6803
  if (trackInfos.length === 0) {
6743
6804
  throw new Error(`Can't initiate negotiation without announcing any tracks`);
@@ -6776,50 +6837,50 @@ class Publisher {
6776
6837
  }
6777
6838
  return sdp;
6778
6839
  };
6840
+ this.extractMid = (sdp, track, trackType) => {
6841
+ if (!sdp) {
6842
+ logger$3('warn', 'No SDP found. Returning empty mid');
6843
+ return '';
6844
+ }
6845
+ logger$3('debug', `No 'mid' found for track. Trying to find it from the Offer SDP`);
6846
+ const parsedSdp = SDP__namespace.parse(sdp);
6847
+ const media = parsedSdp.media.find((m) => {
6848
+ var _a, _b;
6849
+ return (m.type === track.kind &&
6850
+ // if `msid` is not present, we assume that the track is the first one
6851
+ ((_b = (_a = m.msid) === null || _a === void 0 ? void 0 : _a.includes(track.id)) !== null && _b !== void 0 ? _b : true));
6852
+ });
6853
+ if (typeof (media === null || media === void 0 ? void 0 : media.mid) === 'undefined') {
6854
+ logger$3('debug', `No mid found in SDP for track type ${track.kind} and id ${track.id}. Attempting to find a heuristic mid`);
6855
+ const heuristicMid = this.transceiverInitOrder.indexOf(trackType);
6856
+ if (heuristicMid !== -1) {
6857
+ return String(heuristicMid);
6858
+ }
6859
+ logger$3('debug', 'No heuristic mid found. Returning empty mid');
6860
+ return '';
6861
+ }
6862
+ return String(media.mid);
6863
+ };
6779
6864
  this.getCurrentTrackInfos = (sdp) => {
6780
6865
  var _a;
6781
6866
  sdp = sdp || ((_a = this.pc.localDescription) === null || _a === void 0 ? void 0 : _a.sdp);
6782
- const extractMid = (defaultMid, track, trackType) => {
6783
- if (defaultMid)
6784
- return defaultMid;
6785
- if (!sdp) {
6786
- logger$3('warn', 'No SDP found. Returning empty mid');
6787
- return '';
6788
- }
6789
- logger$3('debug', `No 'mid' found for track. Trying to find it from the Offer SDP`);
6790
- const parsedSdp = SDP__namespace.parse(sdp);
6791
- const media = parsedSdp.media.find((m) => {
6792
- var _a, _b;
6793
- return (m.type === track.kind &&
6794
- // if `msid` is not present, we assume that the track is the first one
6795
- ((_b = (_a = m.msid) === null || _a === void 0 ? void 0 : _a.includes(track.id)) !== null && _b !== void 0 ? _b : true));
6796
- });
6797
- if (typeof (media === null || media === void 0 ? void 0 : media.mid) === 'undefined') {
6798
- logger$3('debug', `No mid found in SDP for track type ${track.kind} and id ${track.id}. Attempting to find a heuristic mid`);
6799
- const heuristicMid = this.transceiverInitOrder.indexOf(trackType);
6800
- if (heuristicMid !== -1) {
6801
- return String(heuristicMid);
6802
- }
6803
- logger$3('debug', 'No heuristic mid found. Returning empty mid');
6804
- return '';
6805
- }
6806
- return String(media.mid);
6807
- };
6808
6867
  const { settings } = this.state;
6809
6868
  const targetResolution = settings === null || settings === void 0 ? void 0 : settings.video.target_resolution;
6810
6869
  return this.pc
6811
6870
  .getTransceivers()
6812
6871
  .filter((t) => t.direction === 'sendonly' && t.sender.track)
6813
6872
  .map((transceiver) => {
6873
+ var _a;
6814
6874
  const trackType = Number(Object.keys(this.transceiverRegistry).find((key) => this.transceiverRegistry[key] === transceiver));
6815
6875
  const track = transceiver.sender.track;
6816
6876
  let optimalLayers;
6817
6877
  if (track.readyState === 'live') {
6878
+ const publishOpts = this.publishOptionsPerTrackType.get(trackType);
6818
6879
  optimalLayers =
6819
6880
  trackType === TrackType.VIDEO
6820
6881
  ? findOptimalVideoLayers(track, targetResolution)
6821
6882
  : trackType === TrackType.SCREEN_SHARE
6822
- ? findOptimalScreenSharingLayers(track)
6883
+ ? findOptimalScreenSharingLayers(track, publishOpts === null || publishOpts === void 0 ? void 0 : publishOpts.screenShareSettings)
6823
6884
  : [];
6824
6885
  this.trackLayersCache[trackType] = optimalLayers;
6825
6886
  }
@@ -6838,15 +6899,21 @@ class Publisher {
6838
6899
  height: optimalLayer.height,
6839
6900
  },
6840
6901
  }));
6902
+ const isAudioTrack = [
6903
+ TrackType.AUDIO,
6904
+ TrackType.SCREEN_SHARE_AUDIO,
6905
+ ].includes(trackType);
6906
+ const trackSettings = track.getSettings();
6907
+ // @ts-expect-error - `channelCount` is not defined on `MediaTrackSettings`
6908
+ const isStereo = isAudioTrack && trackSettings.channelCount === 2;
6841
6909
  return {
6842
6910
  trackId: track.id,
6843
6911
  layers: layers,
6844
6912
  trackType,
6845
- mid: extractMid(transceiver.mid, track, trackType),
6846
- // FIXME OL: adjust these values
6847
- stereo: false,
6848
- dtx: TrackType.AUDIO === trackType && this.isDtxEnabled,
6849
- red: TrackType.AUDIO === trackType && this.isRedEnabled,
6913
+ mid: (_a = transceiver.mid) !== null && _a !== void 0 ? _a : this.extractMid(sdp, track, trackType),
6914
+ stereo: isStereo,
6915
+ dtx: isAudioTrack && this.isDtxEnabled,
6916
+ red: isAudioTrack && this.isRedEnabled,
6850
6917
  };
6851
6918
  });
6852
6919
  };
@@ -7064,6 +7131,7 @@ class Subscriber {
7064
7131
  TRACK_TYPE_AUDIO: 'audioStream',
7065
7132
  TRACK_TYPE_VIDEO: 'videoStream',
7066
7133
  TRACK_TYPE_SCREEN_SHARE: 'screenShareStream',
7134
+ TRACK_TYPE_SCREEN_SHARE_AUDIO: 'screenShareAudioStream',
7067
7135
  }[trackType];
7068
7136
  if (!streamKindProp) {
7069
7137
  logger$2('error', `Unknown track type: ${trackType}`);
@@ -7152,7 +7220,8 @@ class Subscriber {
7152
7220
  this.onIceCandidateError = (e) => {
7153
7221
  const errorMessage = e instanceof RTCPeerConnectionIceErrorEvent &&
7154
7222
  `${e.errorCode}: ${e.errorText}`;
7155
- logger$2('error', `ICE Candidate error`, errorMessage);
7223
+ const logLevel = this.pc.iceConnectionState === 'connected' ? 'debug' : 'error';
7224
+ logger$2(logLevel, `ICE Candidate error`, errorMessage);
7156
7225
  };
7157
7226
  this.sfuClient = sfuClient;
7158
7227
  this.dispatcher = dispatcher;
@@ -9665,17 +9734,22 @@ class DynascaleManager {
9665
9734
  *
9666
9735
  * @param audioElement the audio element to bind to.
9667
9736
  * @param sessionId the session id.
9737
+ * @param trackType the kind of audio.
9668
9738
  * @returns a cleanup function that will unbind the audio element.
9669
9739
  */
9670
- this.bindAudioElement = (audioElement, sessionId) => {
9740
+ this.bindAudioElement = (audioElement, sessionId, trackType) => {
9671
9741
  const participant = this.call.state.findParticipantBySessionId(sessionId);
9672
9742
  if (!participant || participant.isLocalParticipant)
9673
9743
  return;
9674
9744
  const participant$ = this.call.state.participants$.pipe(rxjs.map((participants) => participants.find((p) => p.sessionId === sessionId)), rxjs.takeWhile((p) => !!p), rxjs.distinctUntilChanged(), rxjs.shareReplay({ bufferSize: 1, refCount: true }));
9675
9745
  const updateMediaStreamSubscription = participant$
9676
- .pipe(rxjs.distinctUntilKeyChanged('audioStream'))
9746
+ .pipe(rxjs.distinctUntilKeyChanged(trackType === 'screenShareAudioTrack'
9747
+ ? 'screenShareAudioStream'
9748
+ : 'audioStream'))
9677
9749
  .subscribe((p) => {
9678
- const source = p.audioStream;
9750
+ const source = trackType === 'screenShareAudioTrack'
9751
+ ? p.screenShareAudioStream
9752
+ : p.audioStream;
9679
9753
  if (audioElement.srcObject === source)
9680
9754
  return;
9681
9755
  setTimeout(() => {
@@ -9990,7 +10064,16 @@ const getVideoStream = (trackConstraints) => __awaiter(void 0, void 0, void 0, f
9990
10064
  */
9991
10065
  const getScreenShareStream = (options) => __awaiter(void 0, void 0, void 0, function* () {
9992
10066
  try {
9993
- return yield navigator.mediaDevices.getDisplayMedia(Object.assign({ video: true, audio: false }, options));
10067
+ return yield navigator.mediaDevices.getDisplayMedia(Object.assign({ video: true, audio: {
10068
+ channelCount: {
10069
+ ideal: 2,
10070
+ },
10071
+ echoCancellation: false,
10072
+ autoGainControl: false,
10073
+ noiseSuppression: false,
10074
+ },
10075
+ // @ts-expect-error - not present in types yet
10076
+ systemAudio: 'include' }, options));
9994
10077
  }
9995
10078
  catch (e) {
9996
10079
  getLogger(['devices'])('error', 'Failed to get screen share stream', e);
@@ -10119,7 +10202,7 @@ class InputMediaDeviceManager {
10119
10202
  return this.getDevices();
10120
10203
  }
10121
10204
  /**
10122
- * Starts camera/microphone
10205
+ * Starts stream.
10123
10206
  */
10124
10207
  enable() {
10125
10208
  return __awaiter(this, void 0, void 0, function* () {
@@ -10138,9 +10221,7 @@ class InputMediaDeviceManager {
10138
10221
  });
10139
10222
  }
10140
10223
  /**
10141
- * Stops camera/microphone
10142
- *
10143
- * @returns
10224
+ * Stops the stream.
10144
10225
  */
10145
10226
  disable() {
10146
10227
  return __awaiter(this, void 0, void 0, function* () {
@@ -10161,7 +10242,7 @@ class InputMediaDeviceManager {
10161
10242
  });
10162
10243
  }
10163
10244
  /**
10164
- * If status was previously enabled, it will reenable the device.
10245
+ * If status was previously enabled, it will re-enable the device.
10165
10246
  */
10166
10247
  resume() {
10167
10248
  return __awaiter(this, void 0, void 0, function* () {
@@ -10172,9 +10253,8 @@ class InputMediaDeviceManager {
10172
10253
  });
10173
10254
  }
10174
10255
  /**
10175
- * If current device statis is disabled, it will enable the device, else it will disable it.
10176
- *
10177
- * @returns
10256
+ * If the current device status is disabled, it will enable the device,
10257
+ * else it will disable it.
10178
10258
  */
10179
10259
  toggle() {
10180
10260
  return __awaiter(this, void 0, void 0, function* () {
@@ -10186,6 +10266,16 @@ class InputMediaDeviceManager {
10186
10266
  }
10187
10267
  });
10188
10268
  }
10269
+ /**
10270
+ * Will set the default constraints for the device.
10271
+ *
10272
+ * @param constraints the constraints to set.
10273
+ */
10274
+ setDefaultConstraints(constraints) {
10275
+ return __awaiter(this, void 0, void 0, function* () {
10276
+ this.state.setDefaultConstraints(constraints);
10277
+ });
10278
+ }
10189
10279
  /**
10190
10280
  * Select device
10191
10281
  *
@@ -10213,8 +10303,11 @@ class InputMediaDeviceManager {
10213
10303
  }
10214
10304
  });
10215
10305
  }
10306
+ getTracks() {
10307
+ var _a, _b;
10308
+ return (_b = (_a = this.state.mediaStream) === null || _a === void 0 ? void 0 : _a.getTracks()) !== null && _b !== void 0 ? _b : [];
10309
+ }
10216
10310
  muteStream(stopTracks = true) {
10217
- var _a;
10218
10311
  return __awaiter(this, void 0, void 0, function* () {
10219
10312
  if (!this.state.mediaStream) {
10220
10313
  return;
@@ -10224,57 +10317,63 @@ class InputMediaDeviceManager {
10224
10317
  yield this.stopPublishStream(stopTracks);
10225
10318
  }
10226
10319
  this.muteLocalStream(stopTracks);
10227
- if (((_a = this.getTrack()) === null || _a === void 0 ? void 0 : _a.readyState) === 'ended') {
10228
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
10229
- if (typeof this.state.mediaStream.release === 'function') {
10230
- // @ts-expect-error
10231
- this.state.mediaStream.release();
10320
+ this.getTracks().forEach((track) => {
10321
+ if (track.readyState === 'ended') {
10322
+ // @ts-expect-error release() is present in react-native-webrtc
10323
+ // and must be called to dispose the stream
10324
+ if (typeof this.state.mediaStream.release === 'function') {
10325
+ // @ts-expect-error
10326
+ this.state.mediaStream.release();
10327
+ }
10328
+ this.state.setMediaStream(undefined);
10232
10329
  }
10233
- this.state.setMediaStream(undefined);
10234
- }
10330
+ });
10235
10331
  });
10236
10332
  }
10237
- muteTrack() {
10238
- const track = this.getTrack();
10239
- if (!track || !track.enabled) {
10240
- return;
10241
- }
10242
- track.enabled = false;
10333
+ muteTracks() {
10334
+ this.getTracks().forEach((track) => {
10335
+ if (track.enabled)
10336
+ track.enabled = false;
10337
+ });
10243
10338
  }
10244
- unmuteTrack() {
10245
- const track = this.getTrack();
10246
- if (!track || track.enabled) {
10247
- return;
10248
- }
10249
- track.enabled = true;
10339
+ unmuteTracks() {
10340
+ this.getTracks().forEach((track) => {
10341
+ if (!track.enabled)
10342
+ track.enabled = true;
10343
+ });
10250
10344
  }
10251
- stopTrack() {
10252
- const track = this.getTrack();
10253
- if (!track || track.readyState === 'ended') {
10254
- return;
10255
- }
10256
- track.stop();
10345
+ stopTracks() {
10346
+ this.getTracks().forEach((track) => {
10347
+ if (track.readyState === 'live')
10348
+ track.stop();
10349
+ });
10257
10350
  }
10258
10351
  muteLocalStream(stopTracks) {
10259
10352
  if (!this.state.mediaStream) {
10260
10353
  return;
10261
10354
  }
10262
- stopTracks ? this.stopTrack() : this.muteTrack();
10355
+ if (stopTracks) {
10356
+ this.stopTracks();
10357
+ }
10358
+ else {
10359
+ this.muteTracks();
10360
+ }
10263
10361
  }
10264
10362
  unmuteStream() {
10265
- var _a;
10266
10363
  return __awaiter(this, void 0, void 0, function* () {
10267
10364
  this.logger('debug', 'Starting stream');
10268
10365
  let stream;
10269
- if (this.state.mediaStream && ((_a = this.getTrack()) === null || _a === void 0 ? void 0 : _a.readyState) === 'live') {
10366
+ if (this.state.mediaStream &&
10367
+ this.getTracks().every((t) => t.readyState === 'live')) {
10270
10368
  stream = this.state.mediaStream;
10271
- this.unmuteTrack();
10369
+ this.unmuteTracks();
10272
10370
  }
10273
10371
  else {
10274
10372
  if (this.state.mediaStream) {
10275
- this.stopTrack();
10373
+ this.stopTracks();
10276
10374
  }
10277
- const constraints = { deviceId: this.state.selectedDevice };
10375
+ const defaultConstraints = this.state.defaultConstraints;
10376
+ const constraints = Object.assign(Object.assign({}, defaultConstraints), { deviceId: this.state.selectedDevice });
10278
10377
  stream = yield this.getStream(constraints);
10279
10378
  }
10280
10379
  if (this.call.state.callingState === exports.CallingState.JOINED) {
@@ -10293,6 +10392,26 @@ class InputMediaDeviceManagerState {
10293
10392
  this.statusSubject = new rxjs.BehaviorSubject(undefined);
10294
10393
  this.mediaStreamSubject = new rxjs.BehaviorSubject(undefined);
10295
10394
  this.selectedDeviceSubject = new rxjs.BehaviorSubject(undefined);
10395
+ this.defaultConstraintsSubject = new rxjs.BehaviorSubject(undefined);
10396
+ /**
10397
+ * An Observable that emits the current media stream, or `undefined` if the device is currently disabled.
10398
+ *
10399
+ */
10400
+ this.mediaStream$ = this.mediaStreamSubject.asObservable();
10401
+ /**
10402
+ * An Observable that emits the currently selected device
10403
+ */
10404
+ this.selectedDevice$ = this.selectedDeviceSubject
10405
+ .asObservable()
10406
+ .pipe(rxjs.distinctUntilChanged());
10407
+ /**
10408
+ * An Observable that emits the device status
10409
+ */
10410
+ this.status$ = this.statusSubject.asObservable().pipe(rxjs.distinctUntilChanged());
10411
+ /**
10412
+ * The default constraints for the device.
10413
+ */
10414
+ this.defaultConstraints$ = this.defaultConstraintsSubject.asObservable();
10296
10415
  /**
10297
10416
  * Gets the current value of an observable, or undefined if the observable has
10298
10417
  * not emitted a value yet.
@@ -10312,13 +10431,6 @@ class InputMediaDeviceManagerState {
10312
10431
  * @return the updated value.
10313
10432
  */
10314
10433
  this.setCurrentValue = setCurrentValue;
10315
- this.mediaStream$ = this.mediaStreamSubject.asObservable();
10316
- this.selectedDevice$ = this.selectedDeviceSubject
10317
- .asObservable()
10318
- .pipe(rxjs.distinctUntilChanged());
10319
- this.status$ = this.statusSubject
10320
- .asObservable()
10321
- .pipe(rxjs.distinctUntilChanged());
10322
10434
  }
10323
10435
  /**
10324
10436
  * The device status
@@ -10362,6 +10474,21 @@ class InputMediaDeviceManagerState {
10362
10474
  setDevice(deviceId) {
10363
10475
  this.setCurrentValue(this.selectedDeviceSubject, deviceId);
10364
10476
  }
10477
+ /**
10478
+ * Gets the default constraints for the device.
10479
+ */
10480
+ get defaultConstraints() {
10481
+ return this.getCurrentValue(this.defaultConstraints$);
10482
+ }
10483
+ /**
10484
+ * Sets the default constraints for the device.
10485
+ *
10486
+ * @internal
10487
+ * @param constraints the constraints to set.
10488
+ */
10489
+ setDefaultConstraints(constraints) {
10490
+ this.setCurrentValue(this.defaultConstraintsSubject, constraints);
10491
+ }
10365
10492
  }
10366
10493
 
10367
10494
  class CameraManagerState extends InputMediaDeviceManagerState {
@@ -10486,10 +10613,6 @@ class CameraManager extends InputMediaDeviceManager {
10486
10613
  stopPublishStream(stopTracks) {
10487
10614
  return this.call.stopPublish(TrackType.VIDEO, stopTracks);
10488
10615
  }
10489
- getTrack() {
10490
- var _a;
10491
- return (_a = this.state.mediaStream) === null || _a === void 0 ? void 0 : _a.getVideoTracks()[0];
10492
- }
10493
10616
  }
10494
10617
 
10495
10618
  class MicrophoneManagerState extends InputMediaDeviceManagerState {
@@ -10614,10 +10737,6 @@ class MicrophoneManager extends InputMediaDeviceManager {
10614
10737
  stopPublishStream(stopTracks) {
10615
10738
  return this.call.stopPublish(TrackType.AUDIO, stopTracks);
10616
10739
  }
10617
- getTrack() {
10618
- var _a;
10619
- return (_a = this.state.mediaStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0];
10620
- }
10621
10740
  startSpeakingWhileMutedDetection(deviceId) {
10622
10741
  return __awaiter(this, void 0, void 0, function* () {
10623
10742
  if (isReactNative()) {
@@ -10649,6 +10768,128 @@ class MicrophoneManager extends InputMediaDeviceManager {
10649
10768
  }
10650
10769
  }
10651
10770
 
10771
+ class ScreenShareState extends InputMediaDeviceManagerState {
10772
+ constructor() {
10773
+ super(...arguments);
10774
+ this.audioEnabledSubject = new rxjs.BehaviorSubject(true);
10775
+ this.settingsSubject = new rxjs.BehaviorSubject(undefined);
10776
+ /**
10777
+ * An Observable that emits the current screen share audio status.
10778
+ */
10779
+ this.audioEnabled$ = this.audioEnabledSubject
10780
+ .asObservable()
10781
+ .pipe(operators.distinctUntilChanged());
10782
+ /**
10783
+ * An Observable that emits the current screen share settings.
10784
+ */
10785
+ this.settings$ = this.settingsSubject.asObservable();
10786
+ /**
10787
+ * @internal
10788
+ */
10789
+ this.getDeviceIdFromStream = (stream) => {
10790
+ const [track] = stream.getTracks();
10791
+ return track === null || track === void 0 ? void 0 : track.getSettings().deviceId;
10792
+ };
10793
+ }
10794
+ /**
10795
+ * The current screen share audio status.
10796
+ */
10797
+ get audioEnabled() {
10798
+ return this.getCurrentValue(this.audioEnabled$);
10799
+ }
10800
+ /**
10801
+ * Set the current screen share audio status.
10802
+ */
10803
+ setAudioEnabled(isEnabled) {
10804
+ this.setCurrentValue(this.audioEnabledSubject, isEnabled);
10805
+ }
10806
+ /**
10807
+ * The current screen share settings.
10808
+ */
10809
+ get settings() {
10810
+ return this.getCurrentValue(this.settings$);
10811
+ }
10812
+ /**
10813
+ * Set the current screen share settings.
10814
+ *
10815
+ * @param settings the screen share settings to set.
10816
+ */
10817
+ setSettings(settings) {
10818
+ this.setCurrentValue(this.settingsSubject, settings);
10819
+ }
10820
+ }
10821
+
10822
+ class ScreenShareManager extends InputMediaDeviceManager {
10823
+ constructor(call) {
10824
+ super(call, new ScreenShareState(), TrackType.SCREEN_SHARE);
10825
+ }
10826
+ /**
10827
+ * Will enable screen share audio options on supported platforms.
10828
+ *
10829
+ * Note: for ongoing screen share, audio won't be enabled until you
10830
+ * re-publish the screen share stream.
10831
+ */
10832
+ enableScreenShareAudio() {
10833
+ this.state.setAudioEnabled(true);
10834
+ }
10835
+ /**
10836
+ * Will disable screen share audio options on supported platforms.
10837
+ */
10838
+ disableScreenShareAudio() {
10839
+ var _a;
10840
+ return __awaiter(this, void 0, void 0, function* () {
10841
+ this.state.setAudioEnabled(false);
10842
+ if ((_a = this.call.publisher) === null || _a === void 0 ? void 0 : _a.isPublishing(TrackType.SCREEN_SHARE_AUDIO)) {
10843
+ yield this.call.stopPublish(TrackType.SCREEN_SHARE_AUDIO, true);
10844
+ }
10845
+ });
10846
+ }
10847
+ /**
10848
+ * Returns the current screen share settings.
10849
+ */
10850
+ getSettings() {
10851
+ return this.state.settings;
10852
+ }
10853
+ /**
10854
+ * Sets the current screen share settings.
10855
+ *
10856
+ * @param settings the settings to set.
10857
+ */
10858
+ setSettings(settings) {
10859
+ this.state.setSettings(settings);
10860
+ }
10861
+ getDevices() {
10862
+ return rxjs.of([]); // there are no devices to be listed for Screen Share
10863
+ }
10864
+ getStream(constraints) {
10865
+ if (!this.state.audioEnabled) {
10866
+ constraints.audio = false;
10867
+ }
10868
+ return getScreenShareStream(constraints);
10869
+ }
10870
+ publishStream(stream) {
10871
+ return this.call.publishScreenShareStream(stream, {
10872
+ screenShareSettings: this.state.settings,
10873
+ });
10874
+ }
10875
+ stopPublishStream(stopTracks) {
10876
+ return __awaiter(this, void 0, void 0, function* () {
10877
+ yield this.call.stopPublish(TrackType.SCREEN_SHARE, stopTracks);
10878
+ yield this.call.stopPublish(TrackType.SCREEN_SHARE_AUDIO, stopTracks);
10879
+ });
10880
+ }
10881
+ /**
10882
+ * Overrides the default `select` method to throw an error.
10883
+ *
10884
+ * @param deviceId ignored.
10885
+ */
10886
+ select(deviceId) {
10887
+ return __awaiter(this, void 0, void 0, function* () {
10888
+ throw new Error('This method is not supported in for Screen Share');
10889
+ });
10890
+ }
10891
+ }
10892
+
10652
10893
  class SpeakerState {
10653
10894
  constructor() {
10654
10895
  this.selectedDeviceSubject = new rxjs.BehaviorSubject('');
@@ -11022,7 +11263,17 @@ class Call {
11022
11263
  // as the underlying peer connection will take care of it as part
11023
11264
  // of the ice-restart process
11024
11265
  if (localParticipant && !migrate) {
11025
- const { audioStream, videoStream, screenShareStream: screenShare, } = localParticipant;
11266
+ const { audioStream, videoStream, screenShareStream, screenShareAudioStream, } = localParticipant;
11267
+ let screenShare;
11268
+ if (screenShareStream || screenShareAudioStream) {
11269
+ screenShare = new MediaStream();
11270
+ screenShareStream === null || screenShareStream === void 0 ? void 0 : screenShareStream.getVideoTracks().forEach((track) => {
11271
+ screenShare === null || screenShare === void 0 ? void 0 : screenShare.addTrack(track);
11272
+ });
11273
+ screenShareAudioStream === null || screenShareAudioStream === void 0 ? void 0 : screenShareAudioStream.getAudioTracks().forEach((track) => {
11274
+ screenShare === null || screenShare === void 0 ? void 0 : screenShare.addTrack(track);
11275
+ });
11276
+ }
11026
11277
  // restore previous publishing state
11027
11278
  if (audioStream)
11028
11279
  yield this.publishAudioStream(audioStream);
@@ -11276,7 +11527,6 @@ class Call {
11276
11527
  * Consecutive calls to this method will replace the audio stream that is currently being published.
11277
11528
  * The previous audio stream will be stopped.
11278
11529
  *
11279
- *
11280
11530
  * @param audioStream the audio stream to publish.
11281
11531
  */
11282
11532
  this.publishAudioStream = (audioStream) => __awaiter(this, void 0, void 0, function* () {
@@ -11300,10 +11550,10 @@ class Call {
11300
11550
  * Consecutive calls to this method will replace the previous screen-share stream.
11301
11551
  * The previous screen-share stream will be stopped.
11302
11552
  *
11303
- *
11304
11553
  * @param screenShareStream the screen-share stream to publish.
11554
+ * @param opts the options to use when publishing the stream.
11305
11555
  */
11306
- this.publishScreenShareStream = (screenShareStream) => __awaiter(this, void 0, void 0, function* () {
11556
+ this.publishScreenShareStream = (screenShareStream, opts = {}) => __awaiter(this, void 0, void 0, function* () {
11307
11557
  // we should wait until we get a JoinResponse from the SFU,
11308
11558
  // otherwise we risk breaking the ICETrickle flow.
11309
11559
  yield this.assertCallJoined();
@@ -11316,7 +11566,11 @@ class Call {
11316
11566
  this.logger('error', `There is no video track in the screen share stream to publish`);
11317
11567
  return;
11318
11568
  }
11319
- yield this.publisher.publishStream(screenShareStream, screenShareTrack, TrackType.SCREEN_SHARE);
11569
+ yield this.publisher.publishStream(screenShareStream, screenShareTrack, TrackType.SCREEN_SHARE, opts);
11570
+ const [screenShareAudioTrack] = screenShareStream.getAudioTracks();
11571
+ if (screenShareAudioTrack) {
11572
+ yield this.publisher.publishStream(screenShareStream, screenShareAudioTrack, TrackType.SCREEN_SHARE_AUDIO, opts);
11573
+ }
11320
11574
  });
11321
11575
  /**
11322
11576
  * Stops publishing the given track type to the call, if it is currently being published.
@@ -11401,6 +11655,13 @@ class Call {
11401
11655
  dimension: p.screenShareDimension,
11402
11656
  });
11403
11657
  }
11658
+ if (p.publishedTracks.includes(TrackType.SCREEN_SHARE_AUDIO)) {
11659
+ subscriptions.push({
11660
+ userId: p.userId,
11661
+ sessionId: p.sessionId,
11662
+ trackType: TrackType.SCREEN_SHARE_AUDIO,
11663
+ });
11664
+ }
11404
11665
  }
11405
11666
  // schedule update
11406
11667
  this.trackSubscriptionsSubject.next({ type, data: subscriptions });
@@ -11871,9 +12132,10 @@ class Call {
11871
12132
  *
11872
12133
  * @param audioElement the audio element to bind to.
11873
12134
  * @param sessionId the session id.
12135
+ * @param trackType the kind of audio.
11874
12136
  */
11875
- this.bindAudioElement = (audioElement, sessionId) => {
11876
- const unbind = this.dynascaleManager.bindAudioElement(audioElement, sessionId);
12137
+ this.bindAudioElement = (audioElement, sessionId, trackType = 'audioTrack') => {
12138
+ const unbind = this.dynascaleManager.bindAudioElement(audioElement, sessionId, trackType);
11877
12139
  if (!unbind)
11878
12140
  return;
11879
12141
  this.leaveCallHooks.add(unbind);
@@ -11935,6 +12197,7 @@ class Call {
11935
12197
  this.camera = new CameraManager(this);
11936
12198
  this.microphone = new MicrophoneManager(this);
11937
12199
  this.speaker = new SpeakerManager();
12200
+ this.screenShare = new ScreenShareManager(this);
11938
12201
  }
11939
12202
  registerEffects() {
11940
12203
  this.leaveCallHooks.add(
@@ -13243,7 +13506,7 @@ class WSConnectionFallback {
13243
13506
  }
13244
13507
  }
13245
13508
 
13246
- const version = '0.3.29';
13509
+ const version = '0.3.30';
13247
13510
 
13248
13511
  const logger = getLogger(['location']);
13249
13512
  const HINT_URL = `https://hint.stream-io-video.com/`;
@@ -14249,6 +14512,8 @@ exports.OwnCapability = OwnCapability;
14249
14512
  exports.RecordSettingsRequestModeEnum = RecordSettingsRequestModeEnum;
14250
14513
  exports.RecordSettingsRequestQualityEnum = RecordSettingsRequestQualityEnum;
14251
14514
  exports.RxUtils = rxUtils;
14515
+ exports.ScreenShareManager = ScreenShareManager;
14516
+ exports.ScreenShareState = ScreenShareState;
14252
14517
  exports.SfuEvents = events;
14253
14518
  exports.SfuModels = models;
14254
14519
  exports.SpeakerManager = SpeakerManager;