@stream-io/video-client 0.3.28 → 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 (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +6 -4
  3. package/dist/index.browser.es.js +382 -118
  4. package/dist/index.browser.es.js.map +1 -1
  5. package/dist/index.cjs.js +382 -116
  6. package/dist/index.cjs.js.map +1 -1
  7. package/dist/index.es.js +382 -118
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/src/Call.d.ts +14 -10
  10. package/dist/src/devices/CameraManager.d.ts +0 -1
  11. package/dist/src/devices/InputMediaDeviceManager.d.ts +18 -15
  12. package/dist/src/devices/InputMediaDeviceManagerState.d.ts +22 -6
  13. package/dist/src/devices/MicrophoneManager.d.ts +0 -1
  14. package/dist/src/devices/ScreenShareManager.d.ts +39 -0
  15. package/dist/src/devices/ScreenShareState.d.ts +36 -0
  16. package/dist/src/devices/__tests__/ScreenShareManager.test.d.ts +1 -0
  17. package/dist/src/devices/__tests__/mocks.d.ts +3 -7
  18. package/dist/src/devices/index.d.ts +2 -0
  19. package/dist/src/helpers/DynascaleManager.d.ts +3 -2
  20. package/dist/src/helpers/__tests__/hq-audio-sdp.d.ts +1 -0
  21. package/dist/src/helpers/sdp-munging.d.ts +8 -0
  22. package/dist/src/rtc/Publisher.d.ts +7 -4
  23. package/dist/src/rtc/helpers/tracks.d.ts +2 -1
  24. package/dist/src/rtc/videoLayers.d.ts +2 -1
  25. package/dist/src/types.d.ts +20 -0
  26. package/dist/version.d.ts +1 -1
  27. package/package.json +1 -1
  28. package/src/Call.ts +56 -12
  29. package/src/devices/CameraManager.ts +3 -4
  30. package/src/devices/InputMediaDeviceManager.ts +60 -45
  31. package/src/devices/InputMediaDeviceManagerState.ts +34 -14
  32. package/src/devices/MicrophoneManager.ts +3 -4
  33. package/src/devices/ScreenShareManager.ts +85 -0
  34. package/src/devices/ScreenShareState.ts +63 -0
  35. package/src/devices/__tests__/InputMediaDeviceManager.test.ts +16 -1
  36. package/src/devices/__tests__/ScreenShareManager.test.ts +119 -0
  37. package/src/devices/__tests__/mocks.ts +38 -1
  38. package/src/devices/devices.ts +10 -1
  39. package/src/devices/index.ts +2 -0
  40. package/src/helpers/DynascaleManager.ts +18 -3
  41. package/src/helpers/__tests__/DynascaleManager.test.ts +36 -1
  42. package/src/helpers/__tests__/hq-audio-sdp.ts +332 -0
  43. package/src/helpers/__tests__/sdp-munging.test.ts +13 -1
  44. package/src/helpers/sdp-munging.ts +49 -0
  45. package/src/rtc/Publisher.ts +87 -48
  46. package/src/rtc/Subscriber.ts +4 -1
  47. package/src/rtc/helpers/tracks.ts +16 -6
  48. package/src/rtc/videoLayers.ts +4 -2
  49. package/src/store/CallState.ts +3 -2
  50. package/src/store/__tests__/CallState.test.ts +1 -1
  51. 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;
@@ -8357,8 +8426,9 @@ class CallState {
8357
8426
  };
8358
8427
  this.logger = getLogger(['CallState']);
8359
8428
  this.participants$ = this.participantsSubject.asObservable().pipe(
8360
- // TODO: replace with Array.toSorted once available
8361
- operators.map((ps) => [...ps].sort(this.sortParticipantsBy)), operators.shareReplay({ bufferSize: 1, refCount: true }));
8429
+ // maintain stable-sort by mutating the participants stored
8430
+ // in the original subject
8431
+ operators.map((ps) => ps.sort(this.sortParticipantsBy)), operators.shareReplay({ bufferSize: 1, refCount: true }));
8362
8432
  this.localParticipant$ = this.participants$.pipe(operators.map((participants) => participants.find(isStreamVideoLocalParticipant)), operators.shareReplay({ bufferSize: 1, refCount: true }));
8363
8433
  this.remoteParticipants$ = this.participants$.pipe(operators.map((participants) => participants.filter((p) => !p.isLocalParticipant)), operators.shareReplay({ bufferSize: 1, refCount: true }));
8364
8434
  this.pinnedParticipants$ = this.participants$.pipe(operators.map((participants) => participants.filter((p) => !!p.pin)), operators.shareReplay({ bufferSize: 1, refCount: true }));
@@ -9664,17 +9734,22 @@ class DynascaleManager {
9664
9734
  *
9665
9735
  * @param audioElement the audio element to bind to.
9666
9736
  * @param sessionId the session id.
9737
+ * @param trackType the kind of audio.
9667
9738
  * @returns a cleanup function that will unbind the audio element.
9668
9739
  */
9669
- this.bindAudioElement = (audioElement, sessionId) => {
9740
+ this.bindAudioElement = (audioElement, sessionId, trackType) => {
9670
9741
  const participant = this.call.state.findParticipantBySessionId(sessionId);
9671
9742
  if (!participant || participant.isLocalParticipant)
9672
9743
  return;
9673
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 }));
9674
9745
  const updateMediaStreamSubscription = participant$
9675
- .pipe(rxjs.distinctUntilKeyChanged('audioStream'))
9746
+ .pipe(rxjs.distinctUntilKeyChanged(trackType === 'screenShareAudioTrack'
9747
+ ? 'screenShareAudioStream'
9748
+ : 'audioStream'))
9676
9749
  .subscribe((p) => {
9677
- const source = p.audioStream;
9750
+ const source = trackType === 'screenShareAudioTrack'
9751
+ ? p.screenShareAudioStream
9752
+ : p.audioStream;
9678
9753
  if (audioElement.srcObject === source)
9679
9754
  return;
9680
9755
  setTimeout(() => {
@@ -9989,7 +10064,16 @@ const getVideoStream = (trackConstraints) => __awaiter(void 0, void 0, void 0, f
9989
10064
  */
9990
10065
  const getScreenShareStream = (options) => __awaiter(void 0, void 0, void 0, function* () {
9991
10066
  try {
9992
- 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));
9993
10077
  }
9994
10078
  catch (e) {
9995
10079
  getLogger(['devices'])('error', 'Failed to get screen share stream', e);
@@ -10118,7 +10202,7 @@ class InputMediaDeviceManager {
10118
10202
  return this.getDevices();
10119
10203
  }
10120
10204
  /**
10121
- * Starts camera/microphone
10205
+ * Starts stream.
10122
10206
  */
10123
10207
  enable() {
10124
10208
  return __awaiter(this, void 0, void 0, function* () {
@@ -10137,9 +10221,7 @@ class InputMediaDeviceManager {
10137
10221
  });
10138
10222
  }
10139
10223
  /**
10140
- * Stops camera/microphone
10141
- *
10142
- * @returns
10224
+ * Stops the stream.
10143
10225
  */
10144
10226
  disable() {
10145
10227
  return __awaiter(this, void 0, void 0, function* () {
@@ -10160,7 +10242,7 @@ class InputMediaDeviceManager {
10160
10242
  });
10161
10243
  }
10162
10244
  /**
10163
- * If status was previously enabled, it will reenable the device.
10245
+ * If status was previously enabled, it will re-enable the device.
10164
10246
  */
10165
10247
  resume() {
10166
10248
  return __awaiter(this, void 0, void 0, function* () {
@@ -10171,9 +10253,8 @@ class InputMediaDeviceManager {
10171
10253
  });
10172
10254
  }
10173
10255
  /**
10174
- * If current device statis is disabled, it will enable the device, else it will disable it.
10175
- *
10176
- * @returns
10256
+ * If the current device status is disabled, it will enable the device,
10257
+ * else it will disable it.
10177
10258
  */
10178
10259
  toggle() {
10179
10260
  return __awaiter(this, void 0, void 0, function* () {
@@ -10185,6 +10266,16 @@ class InputMediaDeviceManager {
10185
10266
  }
10186
10267
  });
10187
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
+ }
10188
10279
  /**
10189
10280
  * Select device
10190
10281
  *
@@ -10212,8 +10303,11 @@ class InputMediaDeviceManager {
10212
10303
  }
10213
10304
  });
10214
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
+ }
10215
10310
  muteStream(stopTracks = true) {
10216
- var _a;
10217
10311
  return __awaiter(this, void 0, void 0, function* () {
10218
10312
  if (!this.state.mediaStream) {
10219
10313
  return;
@@ -10223,57 +10317,63 @@ class InputMediaDeviceManager {
10223
10317
  yield this.stopPublishStream(stopTracks);
10224
10318
  }
10225
10319
  this.muteLocalStream(stopTracks);
10226
- if (((_a = this.getTrack()) === null || _a === void 0 ? void 0 : _a.readyState) === 'ended') {
10227
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
10228
- if (typeof this.state.mediaStream.release === 'function') {
10229
- // @ts-expect-error
10230
- 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);
10231
10329
  }
10232
- this.state.setMediaStream(undefined);
10233
- }
10330
+ });
10234
10331
  });
10235
10332
  }
10236
- muteTrack() {
10237
- const track = this.getTrack();
10238
- if (!track || !track.enabled) {
10239
- return;
10240
- }
10241
- track.enabled = false;
10333
+ muteTracks() {
10334
+ this.getTracks().forEach((track) => {
10335
+ if (track.enabled)
10336
+ track.enabled = false;
10337
+ });
10242
10338
  }
10243
- unmuteTrack() {
10244
- const track = this.getTrack();
10245
- if (!track || track.enabled) {
10246
- return;
10247
- }
10248
- track.enabled = true;
10339
+ unmuteTracks() {
10340
+ this.getTracks().forEach((track) => {
10341
+ if (!track.enabled)
10342
+ track.enabled = true;
10343
+ });
10249
10344
  }
10250
- stopTrack() {
10251
- const track = this.getTrack();
10252
- if (!track || track.readyState === 'ended') {
10253
- return;
10254
- }
10255
- track.stop();
10345
+ stopTracks() {
10346
+ this.getTracks().forEach((track) => {
10347
+ if (track.readyState === 'live')
10348
+ track.stop();
10349
+ });
10256
10350
  }
10257
10351
  muteLocalStream(stopTracks) {
10258
10352
  if (!this.state.mediaStream) {
10259
10353
  return;
10260
10354
  }
10261
- stopTracks ? this.stopTrack() : this.muteTrack();
10355
+ if (stopTracks) {
10356
+ this.stopTracks();
10357
+ }
10358
+ else {
10359
+ this.muteTracks();
10360
+ }
10262
10361
  }
10263
10362
  unmuteStream() {
10264
- var _a;
10265
10363
  return __awaiter(this, void 0, void 0, function* () {
10266
10364
  this.logger('debug', 'Starting stream');
10267
10365
  let stream;
10268
- 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')) {
10269
10368
  stream = this.state.mediaStream;
10270
- this.unmuteTrack();
10369
+ this.unmuteTracks();
10271
10370
  }
10272
10371
  else {
10273
10372
  if (this.state.mediaStream) {
10274
- this.stopTrack();
10373
+ this.stopTracks();
10275
10374
  }
10276
- const constraints = { deviceId: this.state.selectedDevice };
10375
+ const defaultConstraints = this.state.defaultConstraints;
10376
+ const constraints = Object.assign(Object.assign({}, defaultConstraints), { deviceId: this.state.selectedDevice });
10277
10377
  stream = yield this.getStream(constraints);
10278
10378
  }
10279
10379
  if (this.call.state.callingState === exports.CallingState.JOINED) {
@@ -10292,6 +10392,26 @@ class InputMediaDeviceManagerState {
10292
10392
  this.statusSubject = new rxjs.BehaviorSubject(undefined);
10293
10393
  this.mediaStreamSubject = new rxjs.BehaviorSubject(undefined);
10294
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();
10295
10415
  /**
10296
10416
  * Gets the current value of an observable, or undefined if the observable has
10297
10417
  * not emitted a value yet.
@@ -10311,13 +10431,6 @@ class InputMediaDeviceManagerState {
10311
10431
  * @return the updated value.
10312
10432
  */
10313
10433
  this.setCurrentValue = setCurrentValue;
10314
- this.mediaStream$ = this.mediaStreamSubject.asObservable();
10315
- this.selectedDevice$ = this.selectedDeviceSubject
10316
- .asObservable()
10317
- .pipe(rxjs.distinctUntilChanged());
10318
- this.status$ = this.statusSubject
10319
- .asObservable()
10320
- .pipe(rxjs.distinctUntilChanged());
10321
10434
  }
10322
10435
  /**
10323
10436
  * The device status
@@ -10361,6 +10474,21 @@ class InputMediaDeviceManagerState {
10361
10474
  setDevice(deviceId) {
10362
10475
  this.setCurrentValue(this.selectedDeviceSubject, deviceId);
10363
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
+ }
10364
10492
  }
10365
10493
 
10366
10494
  class CameraManagerState extends InputMediaDeviceManagerState {
@@ -10485,10 +10613,6 @@ class CameraManager extends InputMediaDeviceManager {
10485
10613
  stopPublishStream(stopTracks) {
10486
10614
  return this.call.stopPublish(TrackType.VIDEO, stopTracks);
10487
10615
  }
10488
- getTrack() {
10489
- var _a;
10490
- return (_a = this.state.mediaStream) === null || _a === void 0 ? void 0 : _a.getVideoTracks()[0];
10491
- }
10492
10616
  }
10493
10617
 
10494
10618
  class MicrophoneManagerState extends InputMediaDeviceManagerState {
@@ -10613,10 +10737,6 @@ class MicrophoneManager extends InputMediaDeviceManager {
10613
10737
  stopPublishStream(stopTracks) {
10614
10738
  return this.call.stopPublish(TrackType.AUDIO, stopTracks);
10615
10739
  }
10616
- getTrack() {
10617
- var _a;
10618
- return (_a = this.state.mediaStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0];
10619
- }
10620
10740
  startSpeakingWhileMutedDetection(deviceId) {
10621
10741
  return __awaiter(this, void 0, void 0, function* () {
10622
10742
  if (isReactNative()) {
@@ -10648,6 +10768,128 @@ class MicrophoneManager extends InputMediaDeviceManager {
10648
10768
  }
10649
10769
  }
10650
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
+
10651
10893
  class SpeakerState {
10652
10894
  constructor() {
10653
10895
  this.selectedDeviceSubject = new rxjs.BehaviorSubject('');
@@ -11021,7 +11263,17 @@ class Call {
11021
11263
  // as the underlying peer connection will take care of it as part
11022
11264
  // of the ice-restart process
11023
11265
  if (localParticipant && !migrate) {
11024
- 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
+ }
11025
11277
  // restore previous publishing state
11026
11278
  if (audioStream)
11027
11279
  yield this.publishAudioStream(audioStream);
@@ -11275,7 +11527,6 @@ class Call {
11275
11527
  * Consecutive calls to this method will replace the audio stream that is currently being published.
11276
11528
  * The previous audio stream will be stopped.
11277
11529
  *
11278
- *
11279
11530
  * @param audioStream the audio stream to publish.
11280
11531
  */
11281
11532
  this.publishAudioStream = (audioStream) => __awaiter(this, void 0, void 0, function* () {
@@ -11299,10 +11550,10 @@ class Call {
11299
11550
  * Consecutive calls to this method will replace the previous screen-share stream.
11300
11551
  * The previous screen-share stream will be stopped.
11301
11552
  *
11302
- *
11303
11553
  * @param screenShareStream the screen-share stream to publish.
11554
+ * @param opts the options to use when publishing the stream.
11304
11555
  */
11305
- this.publishScreenShareStream = (screenShareStream) => __awaiter(this, void 0, void 0, function* () {
11556
+ this.publishScreenShareStream = (screenShareStream, opts = {}) => __awaiter(this, void 0, void 0, function* () {
11306
11557
  // we should wait until we get a JoinResponse from the SFU,
11307
11558
  // otherwise we risk breaking the ICETrickle flow.
11308
11559
  yield this.assertCallJoined();
@@ -11315,7 +11566,11 @@ class Call {
11315
11566
  this.logger('error', `There is no video track in the screen share stream to publish`);
11316
11567
  return;
11317
11568
  }
11318
- 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
+ }
11319
11574
  });
11320
11575
  /**
11321
11576
  * Stops publishing the given track type to the call, if it is currently being published.
@@ -11400,6 +11655,13 @@ class Call {
11400
11655
  dimension: p.screenShareDimension,
11401
11656
  });
11402
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
+ }
11403
11665
  }
11404
11666
  // schedule update
11405
11667
  this.trackSubscriptionsSubject.next({ type, data: subscriptions });
@@ -11870,9 +12132,10 @@ class Call {
11870
12132
  *
11871
12133
  * @param audioElement the audio element to bind to.
11872
12134
  * @param sessionId the session id.
12135
+ * @param trackType the kind of audio.
11873
12136
  */
11874
- this.bindAudioElement = (audioElement, sessionId) => {
11875
- const unbind = this.dynascaleManager.bindAudioElement(audioElement, sessionId);
12137
+ this.bindAudioElement = (audioElement, sessionId, trackType = 'audioTrack') => {
12138
+ const unbind = this.dynascaleManager.bindAudioElement(audioElement, sessionId, trackType);
11876
12139
  if (!unbind)
11877
12140
  return;
11878
12141
  this.leaveCallHooks.add(unbind);
@@ -11934,6 +12197,7 @@ class Call {
11934
12197
  this.camera = new CameraManager(this);
11935
12198
  this.microphone = new MicrophoneManager(this);
11936
12199
  this.speaker = new SpeakerManager();
12200
+ this.screenShare = new ScreenShareManager(this);
11937
12201
  }
11938
12202
  registerEffects() {
11939
12203
  this.leaveCallHooks.add(
@@ -13242,7 +13506,7 @@ class WSConnectionFallback {
13242
13506
  }
13243
13507
  }
13244
13508
 
13245
- const version = '0.3.28';
13509
+ const version = '0.3.30';
13246
13510
 
13247
13511
  const logger = getLogger(['location']);
13248
13512
  const HINT_URL = `https://hint.stream-io-video.com/`;
@@ -14248,6 +14512,8 @@ exports.OwnCapability = OwnCapability;
14248
14512
  exports.RecordSettingsRequestModeEnum = RecordSettingsRequestModeEnum;
14249
14513
  exports.RecordSettingsRequestQualityEnum = RecordSettingsRequestQualityEnum;
14250
14514
  exports.RxUtils = rxUtils;
14515
+ exports.ScreenShareManager = ScreenShareManager;
14516
+ exports.ScreenShareState = ScreenShareState;
14251
14517
  exports.SfuEvents = events;
14252
14518
  exports.SfuModels = models;
14253
14519
  exports.SpeakerManager = SpeakerManager;