@stream-io/video-client 1.58.0 → 1.59.1

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 (57) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/index.browser.es.js +367 -76
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +378 -74
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.es.js +367 -76
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/src/Call.d.ts +15 -4
  10. package/dist/src/StreamVideoClient.d.ts +9 -1
  11. package/dist/src/coordinator/connection/utils.d.ts +10 -0
  12. package/dist/src/devices/CameraManager.d.ts +2 -1
  13. package/dist/src/devices/DeviceManager.d.ts +1 -1
  14. package/dist/src/devices/MicrophoneManager.d.ts +2 -1
  15. package/dist/src/gen/coordinator/index.d.ts +26 -0
  16. package/dist/src/helpers/MediaStreamRecorder.d.ts +26 -0
  17. package/dist/src/helpers/concurrency.d.ts +8 -0
  18. package/dist/src/helpers/loopback.d.ts +55 -0
  19. package/dist/src/reporting/ClientEventReporter.d.ts +0 -2
  20. package/dist/src/rtc/Subscriber.d.ts +5 -0
  21. package/dist/src/sorting/participants.d.ts +13 -0
  22. package/index.ts +2 -0
  23. package/package.json +1 -1
  24. package/src/Call.ts +156 -134
  25. package/src/StreamVideoClient.ts +35 -8
  26. package/src/__tests__/Call.lifecycle.test.ts +33 -2
  27. package/src/__tests__/Call.publishing.test.ts +42 -1
  28. package/src/__tests__/StreamVideoClient.api.test.ts +33 -0
  29. package/src/__tests__/StreamVideoClient.rewatch.test.ts +213 -0
  30. package/src/__tests__/StreamVideoClient.ringing.test.ts +43 -24
  31. package/src/__tests__/StreamVideoClient.test.ts +14 -12
  32. package/src/coordinator/connection/__tests__/eventListenerIsolation.test.ts +115 -0
  33. package/src/coordinator/connection/client.ts +3 -2
  34. package/src/coordinator/connection/utils.ts +28 -0
  35. package/src/devices/CameraManager.ts +16 -4
  36. package/src/devices/DeviceManager.ts +7 -4
  37. package/src/devices/MicrophoneManager.ts +11 -3
  38. package/src/devices/__tests__/CameraManager.test.ts +27 -1
  39. package/src/devices/__tests__/DeviceManager.test.ts +49 -1
  40. package/src/devices/__tests__/MicrophoneManager.test.ts +23 -1
  41. package/src/events/call.ts +6 -3
  42. package/src/gen/coordinator/index.ts +26 -0
  43. package/src/helpers/MediaStreamRecorder.ts +104 -0
  44. package/src/helpers/__tests__/MediaStreamRecorder.test.ts +324 -0
  45. package/src/helpers/__tests__/concurrency.test.ts +74 -0
  46. package/src/helpers/__tests__/loopback.test.ts +367 -0
  47. package/src/helpers/concurrency.ts +24 -0
  48. package/src/helpers/loopback.ts +200 -0
  49. package/src/reporting/ClientEventReporter.ts +15 -28
  50. package/src/reporting/__tests__/ClientEventReporter.test.ts +3 -7
  51. package/src/rtc/Subscriber.ts +8 -0
  52. package/src/rtc/__tests__/Subscriber.test.ts +101 -0
  53. package/src/sorting/__tests__/presets.test.ts +188 -3
  54. package/src/sorting/__tests__/sorting.test.ts +22 -1
  55. package/src/sorting/participants.ts +20 -0
  56. package/src/sorting/presets.ts +4 -0
  57. package/src/store/__tests__/CallState.test.ts +2 -2
package/dist/index.es.js CHANGED
@@ -7,7 +7,7 @@ import { TwirpFetchTransport, TwirpErrorCode } from '@protobuf-ts/twirp-transpor
7
7
  import * as scopedLogger from '@stream-io/logger';
8
8
  export { LogLevelEnum } from '@stream-io/logger';
9
9
  import { parse, write } from 'sdp-transform';
10
- import { Subject, Observable, combineLatest, BehaviorSubject, ReplaySubject, shareReplay, map, distinctUntilChanged, startWith, takeWhile, distinctUntilKeyChanged, fromEventPattern, concatMap, merge, from, fromEvent, tap, debounceTime, pairwise, firstValueFrom, of } from 'rxjs';
10
+ import { Subject, Observable, combineLatest, BehaviorSubject, ReplaySubject, shareReplay, map, distinctUntilChanged, startWith, takeWhile, distinctUntilKeyChanged, fromEventPattern, concatMap, merge, from, fromEvent, tap, debounceTime, pairwise, firstValueFrom, of, Subscription } from 'rxjs';
11
11
  import { UAParser } from 'ua-parser-js';
12
12
  import { WorkerTimer } from '@stream-io/worker-timer';
13
13
  import https from 'https';
@@ -4412,6 +4412,28 @@ function isErrorResponse(res) {
4412
4412
  function isCloseEvent(res) {
4413
4413
  return res.code !== undefined;
4414
4414
  }
4415
+ /**
4416
+ * Invokes a single event listener in isolation, so that a faulty listener
4417
+ * cannot abort the dispatch loop nor the bookkeeping that follows it.
4418
+ *
4419
+ * @param listener the listener to invoke.
4420
+ * @param event the event to pass to the listener.
4421
+ * @param logger the logger to report listener failures with.
4422
+ */
4423
+ const invokeEventListener = (listener, event, logger) => {
4424
+ try {
4425
+ const result = listener(event);
4426
+ if (result && typeof result.then === 'function') {
4427
+ Promise.resolve(result).catch((error) => report(logger, error, event));
4428
+ }
4429
+ }
4430
+ catch (error) {
4431
+ report(logger, error, event);
4432
+ }
4433
+ };
4434
+ const report = (logger, error, event) => {
4435
+ logger.error('Unhandled error in event listener', error, event);
4436
+ };
4415
4437
 
4416
4438
  /**
4417
4439
  * Checks whether we are using React Native
@@ -5060,7 +5082,7 @@ const toIceCandidate = (iceTrickle) => {
5060
5082
  }
5061
5083
  };
5062
5084
 
5063
- const version = "1.58.0";
5085
+ const version = "1.59.1";
5064
5086
  const [major, minor, patch] = version.split('.');
5065
5087
  let sdkInfo = {
5066
5088
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -5317,6 +5339,27 @@ const withoutConcurrency = createRunner(wrapWithContinuationTracking);
5317
5339
  */
5318
5340
  const withCancellation = createRunner(wrapWithCancellation);
5319
5341
  const pendingPromises = new Map();
5342
+ /**
5343
+ * Wraps an async function so concurrent calls share the in-flight promise
5344
+ * instead of queueing another execution.
5345
+ *
5346
+ * @param cb Async function to wrap.
5347
+ * @returns Wrapped async function.
5348
+ */
5349
+ function singleFlight(cb) {
5350
+ let pending;
5351
+ return (...args) => {
5352
+ if (pending)
5353
+ return pending;
5354
+ const promise = cb(...args).finally(() => {
5355
+ if (pending === promise) {
5356
+ pending = undefined;
5357
+ }
5358
+ });
5359
+ pending = promise;
5360
+ return promise;
5361
+ };
5362
+ }
5320
5363
  function hasPending(tag) {
5321
5364
  return pendingPromises.has(tag);
5322
5365
  }
@@ -5888,6 +5931,27 @@ const publishingAudio = (a, b) => {
5888
5931
  return 1;
5889
5932
  return 0;
5890
5933
  };
5934
+ /**
5935
+ * A comparator which prioritizes participants who are publishing any track
5936
+ * over participants who are not publishing at all.
5937
+ *
5938
+ * Unlike `publishingVideo` and `publishingAudio`, this comparator does not
5939
+ * distinguish between track types. Muting the microphone or turning off the
5940
+ * camera does not change a participant's rank as long as they still publish
5941
+ * something, which keeps the ordering stable across ordinary device toggles.
5942
+ *
5943
+ * @param a the first participant.
5944
+ * @param b the second participant.
5945
+ */
5946
+ const publishing = (a, b) => {
5947
+ const hasA = a.publishedTracks.length > 0;
5948
+ const hasB = b.publishedTracks.length > 0;
5949
+ if (hasA && !hasB)
5950
+ return -1;
5951
+ if (!hasA && hasB)
5952
+ return 1;
5953
+ return 0;
5954
+ };
5891
5955
  /**
5892
5956
  * A comparator which prioritizes participants who are pinned.
5893
5957
  *
@@ -5998,11 +6062,11 @@ const withVideoIngressSource = withParticipantSource(ParticipantSource.RTMP, Par
5998
6062
  /**
5999
6063
  * The default sorting preset.
6000
6064
  */
6001
- const defaultSortPreset = combineComparators(screenSharing, pinned, ifInvisibleBy(combineComparators(dominantSpeaker, speaking, reactionType('raised-hand'), publishingVideo, publishingAudio)));
6065
+ const defaultSortPreset = combineComparators(screenSharing, pinned, publishing, ifInvisibleBy(combineComparators(dominantSpeaker, speaking, reactionType('raised-hand'), publishingVideo, publishingAudio)));
6002
6066
  /**
6003
6067
  * The sorting preset for speaker layout.
6004
6068
  */
6005
- const speakerLayoutSortPreset = combineComparators(screenSharing, pinned, dominantSpeaker, ifInvisibleBy(combineComparators(speaking, reactionType('raised-hand'), withVideoIngressSource, publishingVideo, publishingAudio)));
6069
+ const speakerLayoutSortPreset = combineComparators(screenSharing, pinned, dominantSpeaker, publishing, ifInvisibleBy(combineComparators(speaking, reactionType('raised-hand'), withVideoIngressSource, publishingVideo, publishingAudio)));
6006
6070
  /**
6007
6071
  * The sorting preset for layouts that don't render all participants but
6008
6072
  * instead, render them in pages.
@@ -6011,7 +6075,7 @@ const paginatedLayoutSortPreset = combineComparators(pinned, ifInvisibleOrUnknow
6011
6075
  /**
6012
6076
  * The sorting preset for livestreams and audio rooms.
6013
6077
  */
6014
- const livestreamOrAudioRoomSortPreset = combineComparators(ifInvisibleOrUnknownBy(combineComparators(dominantSpeaker, speaking, reactionType('raised-hand'), withVideoIngressSource, publishingVideo, publishingAudio)), role('admin', 'host', 'speaker'));
6078
+ const livestreamOrAudioRoomSortPreset = combineComparators(publishing, ifInvisibleOrUnknownBy(combineComparators(dominantSpeaker, speaking, reactionType('raised-hand'), withVideoIngressSource, publishingVideo, publishingAudio)), role('admin', 'host', 'speaker'));
6015
6079
 
6016
6080
  /**
6017
6081
  * Returns the default egress object - when no egress data is available.
@@ -9805,6 +9869,11 @@ class Subscriber extends BasePeerConnection {
9805
9869
  throw e;
9806
9870
  }
9807
9871
  };
9872
+ /**
9873
+ * Whether the given stream is one the SFU echoed back to us
9874
+ * (`allowOwnTracksLoopback`) rather than a local capture stream.
9875
+ */
9876
+ isSelfSubscribedStream = (stream) => !!stream && !!this.trackedStreams?.has(stream);
9808
9877
  handleOnTrack = (e) => {
9809
9878
  const { streams, track } = e;
9810
9879
  const [primaryStream] = streams;
@@ -9894,6 +9963,8 @@ class Subscriber extends BasePeerConnection {
9894
9963
  const target = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
9895
9964
  if (!target)
9896
9965
  return;
9966
+ if (target.isLocalParticipant)
9967
+ return;
9897
9968
  this.state.updateParticipant(target.sessionId, (p) => {
9898
9969
  const current = p.interruptedTracks ?? [];
9899
9970
  const has = current.includes(trackType);
@@ -10636,7 +10707,6 @@ const watchCallRejected = (call) => {
10636
10707
  */
10637
10708
  const watchCallEnded = (call) => {
10638
10709
  return function onCallEnded() {
10639
- globalThis.streamRNVideoSDK?.callingX?.endCall(call, 'remote');
10640
10710
  const { callingState } = call.state;
10641
10711
  if (callingState !== CallingState.IDLE &&
10642
10712
  callingState !== CallingState.LEFT) {
@@ -10645,9 +10715,13 @@ const watchCallEnded = (call) => {
10645
10715
  reason: 'call.ended event received',
10646
10716
  });
10647
10717
  call
10648
- .leave({ message: 'call.ended event received', reject: false })
10718
+ .leave({
10719
+ message: 'call.ended event received',
10720
+ reject: false,
10721
+ reason: 'ended',
10722
+ })
10649
10723
  .catch((err) => {
10650
- call.logger.error('Failed to leave call after call.ended ', err);
10724
+ call.logger.error('Failed to leave call after call.ended', err);
10651
10725
  });
10652
10726
  }
10653
10727
  };
@@ -13453,7 +13527,7 @@ class DeviceManager {
13453
13527
  createSyntheticDevice(selectedDevice, deviceKind);
13454
13528
  writePreferences(currentDevice, deviceKey, muted, storageKey);
13455
13529
  }
13456
- async applyPersistedPreferences(enabledInCallType) {
13530
+ async applyPersistedPreferences(enabledInCallType, forceDisabled = false) {
13457
13531
  const deviceKey = this.trackType === TrackType.AUDIO ? 'microphone' : 'camera';
13458
13532
  const preferences = readPreferences(this.devicePersistence.storageKey);
13459
13533
  const preferenceList = toPreferenceList(preferences[deviceKey]);
@@ -13480,13 +13554,13 @@ class DeviceManager {
13480
13554
  }
13481
13555
  const canPublish = this.call.permissionsContext.canPublish(this.trackType);
13482
13556
  if (typeof muted === 'boolean' && enabledInCallType && canPublish) {
13483
- await this.applyMutedState(muted);
13557
+ await this.applyMutedState(muted, forceDisabled);
13484
13558
  appliedMute = true;
13485
13559
  }
13486
13560
  return appliedDevice || appliedMute;
13487
13561
  }
13488
- async applyMutedState(muted) {
13489
- if (this.state.status !== undefined)
13562
+ async applyMutedState(muted, forceDisabled) {
13563
+ if (forceDisabled || this.state.status !== undefined)
13490
13564
  return;
13491
13565
  if (muted) {
13492
13566
  await this.disable();
@@ -13838,8 +13912,9 @@ class CameraManager extends DeviceManager {
13838
13912
  *
13839
13913
  * @param settings the video settings to apply.
13840
13914
  * @param publish whether to publish the stream after applying the settings.
13915
+ * @param forceDisabled whether to keep the camera disabled, regardless of server-side or local preferences.
13841
13916
  */
13842
- async apply(settings, publish) {
13917
+ async apply(settings, publish, forceDisabled = false) {
13843
13918
  // Wait for any in progress camera operation
13844
13919
  await this.statusChangeSettled();
13845
13920
  await this.selectTargetResolution(settings.target_resolution);
@@ -13851,8 +13926,7 @@ class CameraManager extends DeviceManager {
13851
13926
  if (shouldApplyDefaults &&
13852
13927
  this.devicePersistence.enabled &&
13853
13928
  permissionState === 'granted') {
13854
- persistedPreferencesApplied =
13855
- await this.applyPersistedPreferences(enabledInCallType);
13929
+ persistedPreferencesApplied = await this.applyPersistedPreferences(enabledInCallType, forceDisabled);
13856
13930
  }
13857
13931
  // apply a direction and enable the camera only if in "pristine" state,
13858
13932
  // and there are no persisted preferences
@@ -13862,7 +13936,10 @@ class CameraManager extends DeviceManager {
13862
13936
  const direction = settings.camera_facing === 'front' ? 'front' : 'back';
13863
13937
  await this.selectDirection(direction, { enableCamera: false });
13864
13938
  }
13865
- if (canPublish && settings.camera_default_on && enabledInCallType) {
13939
+ if (!forceDisabled &&
13940
+ canPublish &&
13941
+ settings.camera_default_on &&
13942
+ enabledInCallType) {
13866
13943
  await this.enable();
13867
13944
  }
13868
13945
  }
@@ -14457,8 +14534,9 @@ class MicrophoneManager extends AudioDeviceManager {
14457
14534
  * Applies the audio settings to the microphone.
14458
14535
  * @param settings the audio settings to apply.
14459
14536
  * @param publish whether to publish the stream after applying the settings.
14537
+ * @param forceDisabled whether to keep the mic disabled, regardless of server-side or local preferences.
14460
14538
  */
14461
- async apply(settings, publish) {
14539
+ async apply(settings, publish, forceDisabled = false) {
14462
14540
  // Wait for any in progress mic operation
14463
14541
  await this.statusChangeSettled();
14464
14542
  // apply server-side settings only when the device state is pristine
@@ -14470,11 +14548,11 @@ class MicrophoneManager extends AudioDeviceManager {
14470
14548
  if (shouldApplyDefaults &&
14471
14549
  this.devicePersistence.enabled &&
14472
14550
  permissionState === 'granted') {
14473
- persistedPreferencesApplied = await this.applyPersistedPreferences(true);
14551
+ persistedPreferencesApplied = await this.applyPersistedPreferences(true, forceDisabled);
14474
14552
  }
14475
14553
  const canPublish = this.call.permissionsContext.canPublish(this.trackType);
14476
14554
  if (shouldApplyDefaults && !persistedPreferencesApplied) {
14477
- if (canPublish && settings.mic_default_on) {
14555
+ if (!forceDisabled && canPublish && settings.mic_default_on) {
14478
14556
  await this.enable();
14479
14557
  }
14480
14558
  }
@@ -15443,7 +15521,7 @@ class Call {
15443
15521
  }
15444
15522
  }
15445
15523
  }
15446
- globalThis.streamRNVideoSDK?.callingX?.endCall(this);
15524
+ globalThis.streamRNVideoSDK?.callingX?.endCall(this, reason === 'ended' ? 'remote' : undefined);
15447
15525
  this.statsReporter?.stop();
15448
15526
  this.statsReporter = undefined;
15449
15527
  const leaveReason = message ?? reason ?? 'user is leaving the call';
@@ -15606,8 +15684,18 @@ class Call {
15606
15684
  // const calls = useCalls().filter((c) => c.ringing);
15607
15685
  const calls = this.clientStore.calls.filter((c) => c.cid !== this.cid);
15608
15686
  this.clientStore.setCalls([this, ...calls]);
15609
- const skipSpeakerApply = isReactNative();
15610
- await this.applyDeviceConfig(settings, false, skipSpeakerApply);
15687
+ await this.applyDeviceConfig(settings, { publish: false });
15688
+ };
15689
+ /**
15690
+ * Applies a backend-provided call state snapshot
15691
+ * (call, members, own capabilities) to this instance.
15692
+ *
15693
+ * @internal an internal method and should not be used outside the SDK.
15694
+ */
15695
+ updateFromCallStateResponse = (response) => {
15696
+ this.state.updateFromCallResponse(response.call);
15697
+ this.state.setMembers(response.members);
15698
+ this.state.setOwnCapabilities(response.own_capabilities);
15611
15699
  };
15612
15700
  /**
15613
15701
  * Loads the information about the call.
@@ -15620,9 +15708,7 @@ class Call {
15620
15708
  get = async (params) => {
15621
15709
  await this.setup();
15622
15710
  const response = await this.streamClient.get(this.streamClientBasePath, params);
15623
- this.state.updateFromCallResponse(response.call);
15624
- this.state.setMembers(response.members);
15625
- this.state.setOwnCapabilities(response.own_capabilities);
15711
+ this.updateFromCallStateResponse(response);
15626
15712
  if (params?.ring) {
15627
15713
  this.ringingSubject.next(true);
15628
15714
  }
@@ -15630,9 +15716,7 @@ class Call {
15630
15716
  this.watching = true;
15631
15717
  this.clientStore.registerOrUpdateCall(this);
15632
15718
  }
15633
- // Skip speaker setup on RN if ringing was requested or the call is already ringing
15634
- const skipSpeakerApply = isReactNative();
15635
- await this.applyDeviceConfig(response.call.settings, false, skipSpeakerApply);
15719
+ await this.applyDeviceConfig(response.call.settings, { publish: false });
15636
15720
  return response;
15637
15721
  };
15638
15722
  /**
@@ -15643,9 +15727,7 @@ class Call {
15643
15727
  getOrCreate = async (data) => {
15644
15728
  await this.setup();
15645
15729
  const response = await this.streamClient.post(this.streamClientBasePath, data);
15646
- this.state.updateFromCallResponse(response.call);
15647
- this.state.setMembers(response.members);
15648
- this.state.setOwnCapabilities(response.own_capabilities);
15730
+ this.updateFromCallStateResponse(response);
15649
15731
  if (data?.ring) {
15650
15732
  this.ringingSubject.next(true);
15651
15733
  }
@@ -15653,9 +15735,7 @@ class Call {
15653
15735
  this.watching = true;
15654
15736
  this.clientStore.registerOrUpdateCall(this);
15655
15737
  }
15656
- // Skip speaker setup on RN if ringing was requested or the call is already ringing
15657
- const skipSpeakerApply = isReactNative();
15658
- await this.applyDeviceConfig(response.call.settings, false, skipSpeakerApply);
15738
+ await this.applyDeviceConfig(response.call.settings, { publish: false });
15659
15739
  return response;
15660
15740
  };
15661
15741
  /**
@@ -15719,7 +15799,7 @@ class Call {
15719
15799
  *
15720
15800
  * @returns a promise which resolves once the call join-flow has finished.
15721
15801
  */
15722
- join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, allowOwnTracksLoopback = false, ...data } = {}) => {
15802
+ join = singleFlight(async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, allowOwnTracksLoopback = false, ...data } = {}) => {
15723
15803
  const callingState = this.state.callingState;
15724
15804
  if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
15725
15805
  throw new Error(`Illegal State: call.join() shall be called only once`);
@@ -15741,7 +15821,6 @@ class Call {
15741
15821
  callId: this.id,
15742
15822
  getCallSessionId: () => this.state.session?.id ?? '',
15743
15823
  getSfuId: () => this.credentials?.server.edge_name ?? '',
15744
- getUserSessionId: () => this.sfuClient?.sessionId ?? '',
15745
15824
  });
15746
15825
  this.joinResponseTimeout = joinResponseTimeout;
15747
15826
  this.rpcRequestTimeout = rpcRequestTimeout;
@@ -15793,7 +15872,7 @@ class Call {
15793
15872
  callingX?.endCall(this, 'error');
15794
15873
  throw error;
15795
15874
  }
15796
- };
15875
+ });
15797
15876
  /**
15798
15877
  * Will make a single attempt to watch for call related WebSocket events
15799
15878
  * and initiate a call session with the server.
@@ -15965,7 +16044,10 @@ class Call {
15965
16044
  if (!this.deviceSettingsAppliedOnce &&
15966
16045
  this.state.settings &&
15967
16046
  !supersededByLeave()) {
15968
- await this.applyDeviceConfig(this.state.settings, true, false);
16047
+ await this.applyDeviceConfig(this.state.settings, {
16048
+ publish: true,
16049
+ skipSpeakerApply: false,
16050
+ });
15969
16051
  this.deviceSettingsAppliedOnce = true;
15970
16052
  }
15971
16053
  if (!this.callManagerStarted && !supersededByLeave()) {
@@ -16755,12 +16837,16 @@ class Call {
16755
16837
  const streamStateProp = trackTypeToParticipantStreamKey(trackType);
16756
16838
  if (!streamStateProp)
16757
16839
  continue;
16758
- this.state.updateParticipant(sessionId, (p) => ({
16759
- publishedTracks: mediaStream
16760
- ? pushToIfMissing([...p.publishedTracks], trackType)
16761
- : p.publishedTracks.filter((t) => t !== trackType),
16762
- [streamStateProp]: mediaStream,
16763
- }));
16840
+ this.state.updateParticipant(sessionId, (p) => {
16841
+ const currentStream = p[streamStateProp];
16842
+ const isEchoedStream = this.subscriber?.isSelfSubscribedStream(currentStream);
16843
+ return {
16844
+ publishedTracks: mediaStream
16845
+ ? pushToIfMissing([...p.publishedTracks], trackType)
16846
+ : p.publishedTracks.filter((t) => t !== trackType),
16847
+ [streamStateProp]: isEchoedStream ? currentStream : mediaStream,
16848
+ };
16849
+ });
16764
16850
  }
16765
16851
  };
16766
16852
  /**
@@ -17450,18 +17536,21 @@ class Call {
17450
17536
  *
17451
17537
  * @internal
17452
17538
  */
17453
- applyDeviceConfig = async (settings, publish, skipSpeakerApply) => {
17539
+ applyDeviceConfig = async (settings, opts) => {
17540
+ const { publish,
17541
+ // Skip speaker setup on RN until joining (explicitly passed as false during join())
17542
+ skipSpeakerApply = isReactNative(), withDisabledDevices, } = opts;
17454
17543
  if (!skipSpeakerApply) {
17455
- await this.speaker.apply(settings).catch((err) => {
17456
- this.logger.warn('Speaker init failed', err);
17457
- });
17544
+ await this.speaker
17545
+ .apply(settings)
17546
+ .catch((err) => this.logger.warn('Speaker init failed', err));
17458
17547
  }
17459
- await this.camera.apply(settings.video, publish).catch((err) => {
17460
- this.logger.warn('Camera init failed', err);
17461
- });
17462
- await this.microphone.apply(settings.audio, publish).catch((err) => {
17463
- this.logger.warn('Mic init failed', err);
17464
- });
17548
+ await this.camera
17549
+ .apply(settings.video, publish, withDisabledDevices)
17550
+ .catch((err) => this.logger.warn('Camera init failed', err));
17551
+ await this.microphone
17552
+ .apply(settings.audio, publish, withDisabledDevices)
17553
+ .catch((err) => this.logger.warn('Mic init failed', err));
17465
17554
  };
17466
17555
  /**
17467
17556
  * Will begin tracking the given element for visibility changes within the
@@ -18945,11 +19034,11 @@ class StreamClient {
18945
19034
  return;
18946
19035
  // call generic listeners
18947
19036
  for (const listener of this.listeners.all || []) {
18948
- listener(event);
19037
+ invokeEventListener(listener, event, this.logger);
18949
19038
  }
18950
19039
  // call type specific listeners
18951
19040
  for (const listener of this.listeners[event.type] || []) {
18952
- listener(event);
19041
+ invokeEventListener(listener, event, this.logger);
18953
19042
  }
18954
19043
  };
18955
19044
  /**
@@ -18969,11 +19058,11 @@ class StreamClient {
18969
19058
  return await this.wsConnection.connect(this.defaultWSTimeout);
18970
19059
  };
18971
19060
  getSdkVersion = () => this.options.clientAppIdentifier?.sdkVersion ||
18972
- "1.58.0";
19061
+ "1.59.1";
18973
19062
  getUserAgent = () => {
18974
19063
  if (!this.cachedUserAgent) {
18975
19064
  const { clientAppIdentifier = {} } = this.options;
18976
- const { sdkName = 'js', sdkVersion = "1.58.0", ...extras } = clientAppIdentifier;
19065
+ const { sdkName = 'js', sdkVersion = "1.59.1", ...extras } = clientAppIdentifier;
18977
19066
  this.cachedUserAgent = [
18978
19067
  `stream-video-${sdkName}-v${sdkVersion}`,
18979
19068
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
@@ -19336,7 +19425,6 @@ class ClientEventReporter {
19336
19425
  user_id: this.streamClient.userID || this.coordinatorConnectUserId,
19337
19426
  type: ctx?.callType,
19338
19427
  id: ctx?.callId,
19339
- call_cid: cid,
19340
19428
  stage: 'JoinInitiated',
19341
19429
  join_attempt_id: joinAttemptId,
19342
19430
  ...(coordinatorConnectId && {
@@ -19518,7 +19606,6 @@ class ClientEventReporter {
19518
19606
  startedAt: Date.now(),
19519
19607
  joinAttemptIdSnapshot: this.joinAttemptIds.get(cid),
19520
19608
  sfuId: this.getSfuId(cid),
19521
- userSessionId: this.getUserSessionId(cid),
19522
19609
  wasPreviouslyConnected: this.pcEverConnected.get(key) === true,
19523
19610
  lastIceState: iceConnectionState,
19524
19611
  };
@@ -19529,9 +19616,6 @@ class ClientEventReporter {
19529
19616
  peer_connection: role,
19530
19617
  was_previously_connected: pair.wasPreviouslyConnected,
19531
19618
  ...(pair.sfuId && { sfu_id: pair.sfuId }),
19532
- ...(pair.userSessionId && {
19533
- user_session_id: pair.userSessionId,
19534
- }),
19535
19619
  event_type: 'initiated',
19536
19620
  });
19537
19621
  };
@@ -19546,9 +19630,6 @@ class ClientEventReporter {
19546
19630
  peer_connection: role,
19547
19631
  was_previously_connected: pair.wasPreviouslyConnected,
19548
19632
  ...(pair.sfuId && { sfu_id: pair.sfuId }),
19549
- ...(pair.userSessionId && {
19550
- user_session_id: pair.userSessionId,
19551
- }),
19552
19633
  event_type: 'completed',
19553
19634
  outcome: 'success',
19554
19635
  retry_count_attempt: 0,
@@ -19566,9 +19647,6 @@ class ClientEventReporter {
19566
19647
  ...this.sessionIdField(cid),
19567
19648
  peer_connection: role,
19568
19649
  was_previously_connected: pair.wasPreviouslyConnected,
19569
- ...(pair.userSessionId && {
19570
- user_session_id: pair.userSessionId,
19571
- }),
19572
19650
  ...(pair.sfuId && { sfu_id: pair.sfuId }),
19573
19651
  event_type: 'completed',
19574
19652
  outcome: 'failure',
@@ -19581,7 +19659,6 @@ class ClientEventReporter {
19581
19659
  this.peerConnectionPairs.delete(key);
19582
19660
  };
19583
19661
  getSfuId = (cid) => this.callContexts.get(cid)?.getSfuId() ?? '';
19584
- getUserSessionId = (cid) => this.callContexts.get(cid)?.getUserSessionId() ?? '';
19585
19662
  sessionIdField = (cid) => {
19586
19663
  const callSessionId = this.callContexts.get(cid)?.getCallSessionId() ?? '';
19587
19664
  return callSessionId ? { call_session_id: callSessionId } : {};
@@ -19593,7 +19670,6 @@ class ClientEventReporter {
19593
19670
  user_id: this.streamClient.userID || this.coordinatorConnectUserId,
19594
19671
  type: ctx?.callType ?? '',
19595
19672
  id: ctx?.callId ?? '',
19596
- call_cid: cid,
19597
19673
  stage,
19598
19674
  stage_id: pair.sid,
19599
19675
  ...(pair.joinAttemptIdSnapshot && {
@@ -19844,6 +19920,7 @@ class StreamVideoClient {
19844
19920
  }
19845
19921
  else {
19846
19922
  await call.updateFromRingingEvent(e);
19923
+ await call.get();
19847
19924
  }
19848
19925
  }
19849
19926
  else {
@@ -19882,6 +19959,13 @@ class StreamVideoClient {
19882
19959
  this.logger.error(`Failed to init call from event ${e.type}`, err);
19883
19960
  }
19884
19961
  };
19962
+ /**
19963
+ * Queries the API for calls matching the given filters.
19964
+ * @param data the query data.
19965
+ */
19966
+ doQueryCalls = (data) => {
19967
+ return this.streamClient.post('/calls', data);
19968
+ };
19885
19969
  /**
19886
19970
  * Rewatches the given calls with retry logic.
19887
19971
  * @param callsToReWatch array of call IDs to rewatch
@@ -19892,10 +19976,16 @@ class StreamVideoClient {
19892
19976
  for (let attempt = 0; attempt < maxRetries; attempt++) {
19893
19977
  try {
19894
19978
  this.logger.info(`Rewatching calls ${callsToReWatch.join(', ')} attempt ${attempt + 1}`);
19895
- await this.queryCalls({
19979
+ const response = await this.doQueryCalls({
19896
19980
  watch: true,
19897
19981
  filter_conditions: { cid: { $in: callsToReWatch } },
19898
19982
  });
19983
+ for (const c of response.calls) {
19984
+ const call = this.writeableStateStore.findCall(c.call.type, c.call.id);
19985
+ if (call) {
19986
+ call.updateFromCallStateResponse(c);
19987
+ }
19988
+ }
19899
19989
  return;
19900
19990
  }
19901
19991
  catch (err) {
@@ -20038,9 +20128,11 @@ class StreamVideoClient {
20038
20128
  * Will query the API for calls matching the given filters.
20039
20129
  *
20040
20130
  * @param data the query data.
20131
+ * @param opts additional options, for tweaking the API behavior.
20041
20132
  */
20042
- queryCalls = async (data = {}) => {
20043
- const response = await this.streamClient.post('/calls', data);
20133
+ queryCalls = async (data = {}, opts = {}) => {
20134
+ const { withDisabledDevices = true } = opts;
20135
+ const response = await this.doQueryCalls(data);
20044
20136
  const calls = [];
20045
20137
  for (const c of response.calls) {
20046
20138
  const call = new Call({
@@ -20054,7 +20146,10 @@ class StreamVideoClient {
20054
20146
  clientStore: this.writeableStateStore,
20055
20147
  });
20056
20148
  call.state.updateFromCallResponse(c.call);
20057
- await call.applyDeviceConfig(c.call.settings, false, isReactNative());
20149
+ await call.applyDeviceConfig(c.call.settings, {
20150
+ publish: false,
20151
+ withDisabledDevices,
20152
+ });
20058
20153
  if (data.watch) {
20059
20154
  await call.setup();
20060
20155
  this.writeableStateStore.registerCall(call);
@@ -20206,6 +20301,202 @@ const humanize = (n) => {
20206
20301
  return String(n);
20207
20302
  };
20208
20303
 
20304
+ /** Default loopback stream wait timeout. */
20305
+ const LOOPBACK_STREAMS_WAIT_TIMEOUT_MS = 10 * 1000;
20306
+ /** Default loopback recording duration. */
20307
+ const DEFAULT_LOOPBACK_RECORDING_DURATION_MS = 10 * 1000;
20308
+ /** Shortest permitted loopback recording duration. */
20309
+ const MIN_LOOPBACK_RECORDING_DURATION_MS = 5 * 1000;
20310
+ /** Longest permitted loopback recording duration. */
20311
+ const MAX_LOOPBACK_RECORDING_DURATION_MS = 2 * 60 * 1000;
20312
+ /** Thrown when loopback tracks do not arrive in time. */
20313
+ class LoopbackStreamsTimeoutError extends Error {
20314
+ constructor() {
20315
+ super('Timed out waiting for loopback streams. Ensure the call was joined ' +
20316
+ 'with `allowOwnTracksLoopback: true` and that the SFU is configured ' +
20317
+ 'to echo self-sub tracks.');
20318
+ this.name = 'LoopbackStreamsTimeoutError';
20319
+ }
20320
+ }
20321
+ /**
20322
+ * Returns the streams on the given participant that the SFU echoed back to us,
20323
+ * ignoring the local capture streams the publishing path writes to the same
20324
+ * fields.
20325
+ */
20326
+ const getLoopbackStreams = (call, participant) => {
20327
+ const { subscriber } = call ?? {};
20328
+ const { audioStream, videoStream } = participant ?? {};
20329
+ return {
20330
+ loopbackVideoStream: subscriber?.isSelfSubscribedStream(videoStream)
20331
+ ? videoStream
20332
+ : undefined,
20333
+ loopbackAudioStream: subscriber?.isSelfSubscribedStream(audioStream)
20334
+ ? audioStream
20335
+ : undefined,
20336
+ };
20337
+ };
20338
+ /**
20339
+ * Returns loopback tracks when all required tracks are present.
20340
+ *
20341
+ * Prefer {@link waitForLoopbackStreams}, which observes call state until the
20342
+ * tracks arrive. This is the synchronous snapshot it is built from.
20343
+ *
20344
+ * @internal
20345
+ */
20346
+ const getLoopbackTracks = (call, participant, includeVideo) => {
20347
+ if (!participant)
20348
+ return undefined;
20349
+ const { loopbackAudioStream, loopbackVideoStream } = getLoopbackStreams(call, participant);
20350
+ const audioTrack = loopbackAudioStream?.getAudioTracks()[0];
20351
+ const videoTrack = includeVideo
20352
+ ? loopbackVideoStream?.getVideoTracks()[0]
20353
+ : undefined;
20354
+ if (!audioTrack || (includeVideo && !videoTrack)) {
20355
+ return undefined;
20356
+ }
20357
+ return { audioTrack, videoTrack };
20358
+ };
20359
+ /** Waits for the requested loopback tracks. */
20360
+ const waitForLoopbackStreams = (call, opts) => {
20361
+ const callState = call.state;
20362
+ const { includeVideo, signal, timeoutMs = LOOPBACK_STREAMS_WAIT_TIMEOUT_MS, } = opts;
20363
+ return new Promise((resolve, reject) => {
20364
+ const initial = getLoopbackTracks(call, callState.localParticipant, includeVideo);
20365
+ if (initial) {
20366
+ resolve(initial);
20367
+ return;
20368
+ }
20369
+ if (signal.aborted) {
20370
+ resolve(null);
20371
+ return;
20372
+ }
20373
+ let settled = false;
20374
+ const subscriptions = new Subscription();
20375
+ const cleanup = () => {
20376
+ if (settled)
20377
+ return;
20378
+ settled = true;
20379
+ clearTimeout(timeoutId);
20380
+ signal.removeEventListener('abort', onAbort);
20381
+ subscriptions.unsubscribe();
20382
+ };
20383
+ const onAbort = () => {
20384
+ cleanup();
20385
+ resolve(null);
20386
+ };
20387
+ const timeoutId = setTimeout(() => {
20388
+ cleanup();
20389
+ reject(new LoopbackStreamsTimeoutError());
20390
+ }, timeoutMs);
20391
+ signal.addEventListener('abort', onAbort);
20392
+ subscriptions.add(callState.localParticipant$.subscribe((participant) => {
20393
+ const ready = getLoopbackTracks(call, participant, includeVideo);
20394
+ if (ready) {
20395
+ cleanup();
20396
+ resolve(ready);
20397
+ }
20398
+ }));
20399
+ });
20400
+ };
20401
+ /** Enables loopback audio while `fn` runs, then restores it. */
20402
+ const withLoopbackAudioEnabled = async (tracks, fn) => {
20403
+ const restore = enableLoopbackAudio(tracks);
20404
+ try {
20405
+ return await fn();
20406
+ }
20407
+ finally {
20408
+ restore();
20409
+ }
20410
+ };
20411
+ /** Enables loopback audio and returns a restore function. */
20412
+ const enableLoopbackAudio = (tracks) => {
20413
+ const { audioTrack } = tracks;
20414
+ const previous = audioTrack?.enabled ?? false;
20415
+ if (audioTrack) {
20416
+ audioTrack.enabled = true;
20417
+ }
20418
+ return () => {
20419
+ if (audioTrack) {
20420
+ audioTrack.enabled = previous;
20421
+ }
20422
+ };
20423
+ };
20424
+ /** Clamps and rounds a loopback recording duration. */
20425
+ const clampLoopbackRecordingDuration = (durationMs) => Math.round(Math.min(MAX_LOOPBACK_RECORDING_DURATION_MS, Math.max(MIN_LOOPBACK_RECORDING_DURATION_MS, durationMs)));
20426
+
20427
+ /** Single-use `MediaRecorder` wrapper that returns an in-memory blob. */
20428
+ class MediaStreamRecorder {
20429
+ recording = promiseWithResolvers();
20430
+ logger = videoLoggerSystem.getLogger('MediaStreamRecorder');
20431
+ chunks = [];
20432
+ timeoutId;
20433
+ recorder;
20434
+ tracks;
20435
+ maxDurationMs;
20436
+ constructor(stream, options = {}) {
20437
+ if (typeof MediaRecorder === 'undefined') {
20438
+ throw new Error('MediaRecorder is not available in this environment');
20439
+ }
20440
+ this.tracks = stream.getTracks();
20441
+ if (this.tracks.length === 0) {
20442
+ throw new Error('cannot record a MediaStream that has no tracks');
20443
+ }
20444
+ this.maxDurationMs = options.maxDurationMs;
20445
+ this.recorder = new MediaRecorder(new MediaStream(this.tracks));
20446
+ this.recorder.ondataavailable = (event) => {
20447
+ if (event.data?.size)
20448
+ this.chunks.push(event.data);
20449
+ };
20450
+ this.recorder.onstop = this.handleStop;
20451
+ this.recorder.onerror = this.handleError;
20452
+ }
20453
+ /** Starts recording and resolves when recording stops. */
20454
+ start = () => {
20455
+ if (this.isSettled()) {
20456
+ throw new Error('MediaStreamRecorder is single-use: this instance has already ' +
20457
+ 'recorded. Construct a new one.');
20458
+ }
20459
+ if (this.recorder.state === 'inactive') {
20460
+ this.tracks.forEach((track) => track.addEventListener('ended', this.stop));
20461
+ if (this.maxDurationMs !== undefined) {
20462
+ this.timeoutId = setTimeout(this.stop, this.maxDurationMs);
20463
+ }
20464
+ this.recorder.start();
20465
+ }
20466
+ return this.recording.promise;
20467
+ };
20468
+ /** Stops recording early. */
20469
+ stop = () => {
20470
+ if (this.recorder.state !== 'inactive')
20471
+ this.recorder.stop();
20472
+ };
20473
+ isSettled = () => this.recording.isResolved() || this.recording.isRejected();
20474
+ detach = () => {
20475
+ clearTimeout(this.timeoutId);
20476
+ this.tracks.forEach((track) => track.removeEventListener('ended', this.stop));
20477
+ };
20478
+ handleStop = () => {
20479
+ this.detach();
20480
+ if (this.chunks.length === 0) {
20481
+ this.logger.warn('MediaRecorder recording produced no data');
20482
+ this.recording.resolve(null);
20483
+ return;
20484
+ }
20485
+ const mimeType = this.chunks[0].type || this.recorder.mimeType;
20486
+ this.recording.resolve({
20487
+ blob: new Blob(this.chunks, { type: mimeType }),
20488
+ });
20489
+ };
20490
+ handleError = (event) => {
20491
+ if (this.isSettled())
20492
+ return;
20493
+ this.detach();
20494
+ const error = event.error;
20495
+ this.logger.error('MediaRecorder error', error);
20496
+ this.recording.reject(error ?? new Error('MediaRecorder failed'));
20497
+ };
20498
+ }
20499
+
20209
20500
  /**
20210
20501
  * Tiny, type-safe event emitter.
20211
20502
  *
@@ -20557,5 +20848,5 @@ var e2eeWorker$1 = /*#__PURE__*/Object.freeze({
20557
20848
  e2eeWorker: e2eeWorker
20558
20849
  });
20559
20850
 
20560
- export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, EncryptionManager, EncryptionSettingsRequestModeEnum, EncryptionSettingsResponseModeEnum, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, HLSSettingsRequestQualityTracksEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsRequestOutputTypesEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RawRecordingSettingsRequestModeEnum, RawRecordingSettingsResponseModeEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, RequestPermissionRequestPermissionsEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, SfuJoinError, models as SfuModels, SfuTimeoutError, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, TranslationSettingsLanguagesEnum, UpdateUserPermissionsRequestGrantPermissionsEnum, UpdateUserPermissionsRequestRevokePermissionsEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, WSAuthMessageProductsEnum, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getCallMediaEngineProvider, getClientDetails, getDeviceState, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio$1 as hasAudio, hasInterruptedTrack, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, humanize, isPinned, livestreamOrAudioRoomSortPreset, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setCallMediaEngineProvider, setDeviceInfo, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, videoLoggerSystem, withParticipantSource };
20851
+ export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DEFAULT_LOOPBACK_RECORDING_DURATION_MS, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, EncryptionManager, EncryptionSettingsRequestModeEnum, EncryptionSettingsResponseModeEnum, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, HLSSettingsRequestQualityTracksEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsRequestOutputTypesEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LOOPBACK_STREAMS_WAIT_TIMEOUT_MS, LayoutSettingsRequestNameEnum, LoopbackStreamsTimeoutError, MAX_LOOPBACK_RECORDING_DURATION_MS, MIN_LOOPBACK_RECORDING_DURATION_MS, MediaStreamRecorder, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RawRecordingSettingsRequestModeEnum, RawRecordingSettingsResponseModeEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, RequestPermissionRequestPermissionsEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, SfuJoinError, models as SfuModels, SfuTimeoutError, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, TranslationSettingsLanguagesEnum, UpdateUserPermissionsRequestGrantPermissionsEnum, UpdateUserPermissionsRequestRevokePermissionsEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, WSAuthMessageProductsEnum, checkIfAudioOutputChangeSupported, clampLoopbackRecordingDuration, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, enableLoopbackAudio, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getCallMediaEngineProvider, getClientDetails, getDeviceState, getLoopbackStreams, getLoopbackTracks, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio$1 as hasAudio, hasInterruptedTrack, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, humanize, isPinned, livestreamOrAudioRoomSortPreset, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishing, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setCallMediaEngineProvider, setDeviceInfo, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, videoLoggerSystem, waitForLoopbackStreams, withLoopbackAudioEnabled, withParticipantSource };
20561
20852
  //# sourceMappingURL=index.es.js.map