@stream-io/video-client 1.4.1 → 1.4.3

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 (42) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.browser.es.js +219 -144
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +219 -142
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +219 -144
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/Call.d.ts +7 -7
  9. package/dist/src/StreamSfuClient.d.ts +7 -7
  10. package/dist/src/StreamVideoClient.d.ts +5 -5
  11. package/dist/src/coordinator/connection/client.d.ts +13 -14
  12. package/dist/src/coordinator/connection/connection.d.ts +3 -5
  13. package/dist/src/coordinator/connection/insights.d.ts +0 -1
  14. package/dist/src/devices/BrowserPermission.d.ts +24 -0
  15. package/dist/src/devices/InputMediaDeviceManagerState.d.ts +3 -3
  16. package/dist/src/devices/devices.d.ts +30 -11
  17. package/dist/src/gen/coordinator/index.d.ts +5 -0
  18. package/dist/src/helpers/ViewportTracker.d.ts +1 -1
  19. package/dist/src/helpers/lazy.d.ts +4 -0
  20. package/dist/src/helpers/sdp-munging.d.ts +2 -2
  21. package/dist/src/rtc/Dispatcher.d.ts +2 -2
  22. package/dist/src/rtc/codecs.d.ts +1 -1
  23. package/dist/src/rtc/signal.d.ts +0 -1
  24. package/dist/src/stats/utils.d.ts +4 -4
  25. package/dist/src/store/CallState.d.ts +1 -1
  26. package/package.json +4 -4
  27. package/src/devices/BrowserPermission.ts +152 -0
  28. package/src/devices/CameraManagerState.ts +2 -6
  29. package/src/devices/InputMediaDeviceManagerState.ts +10 -44
  30. package/src/devices/MicrophoneManagerState.ts +2 -6
  31. package/src/devices/__tests__/CameraManager.test.ts +3 -0
  32. package/src/devices/__tests__/InputMediaDeviceManager.test.ts +5 -3
  33. package/src/devices/__tests__/InputMediaDeviceManagerFilters.test.ts +6 -2
  34. package/src/devices/__tests__/InputMediaDeviceManagerState.test.ts +41 -51
  35. package/src/devices/__tests__/MicrophoneManager.test.ts +4 -1
  36. package/src/devices/__tests__/MicrophoneManagerRN.test.ts +8 -1
  37. package/src/devices/__tests__/SpeakerManager.test.ts +8 -1
  38. package/src/devices/__tests__/mocks.ts +6 -1
  39. package/src/devices/devices.ts +113 -112
  40. package/src/gen/coordinator/index.ts +5 -0
  41. package/src/helpers/RNSpeechDetector.ts +1 -1
  42. package/src/helpers/lazy.ts +15 -0
package/dist/index.cjs.js CHANGED
@@ -143,6 +143,11 @@ const RecordSettingsRequestQualityEnum = {
143
143
  _720P: '720p',
144
144
  _1080P: '1080p',
145
145
  _1440P: '1440p',
146
+ PORTRAIT_360X640: 'portrait-360x640',
147
+ PORTRAIT_480X854: 'portrait-480x854',
148
+ PORTRAIT_720X1280: 'portrait-720x1280',
149
+ PORTRAIT_1080X1920: 'portrait-1080x1920',
150
+ PORTRAIT_1440X2560: 'portrait-1440x2560',
146
151
  };
147
152
  /**
148
153
  * @export
@@ -10828,6 +10833,127 @@ const CallTypes = new CallTypesRegistry([
10828
10833
  }),
10829
10834
  ]);
10830
10835
 
10836
+ class BrowserPermission {
10837
+ constructor(permission) {
10838
+ this.permission = permission;
10839
+ this.disposeController = new AbortController();
10840
+ this.wasPrompted = false;
10841
+ this.listeners = new Set();
10842
+ this.logger = getLogger(['permissions']);
10843
+ const signal = this.disposeController.signal;
10844
+ this.ready = (async () => {
10845
+ const assumeGranted = (error) => {
10846
+ this.logger('warn', "Can't query permissions, assuming granted", {
10847
+ permission,
10848
+ error,
10849
+ });
10850
+ this.setState('granted');
10851
+ };
10852
+ if (!canQueryPermissions()) {
10853
+ return assumeGranted();
10854
+ }
10855
+ try {
10856
+ const status = await navigator.permissions.query({
10857
+ name: permission.queryName,
10858
+ });
10859
+ if (!signal.aborted) {
10860
+ this.setState(status.state);
10861
+ status.addEventListener('change', () => this.setState(status.state), {
10862
+ signal,
10863
+ });
10864
+ }
10865
+ }
10866
+ catch (err) {
10867
+ assumeGranted(err);
10868
+ }
10869
+ })();
10870
+ }
10871
+ dispose() {
10872
+ this.state = undefined;
10873
+ this.disposeController.abort();
10874
+ }
10875
+ async getState() {
10876
+ await this.ready;
10877
+ if (!this.state) {
10878
+ throw new Error('BrowserPermission instance possibly disposed');
10879
+ }
10880
+ return this.state;
10881
+ }
10882
+ async prompt({ forcePrompt = false, throwOnNotAllowed = false, } = {}) {
10883
+ await withoutConcurrency(`permission-prompt-${this.permission.queryName}`, async () => {
10884
+ if ((await this.getState()) !== 'prompt' ||
10885
+ (this.wasPrompted && !forcePrompt)) {
10886
+ const isGranted = this.state === 'granted';
10887
+ if (!isGranted && throwOnNotAllowed) {
10888
+ throw new DOMException('Permission was not granted previously, and prompting again is not allowed', 'NotAllowedError');
10889
+ }
10890
+ return isGranted;
10891
+ }
10892
+ try {
10893
+ this.wasPrompted = true;
10894
+ const stream = await navigator.mediaDevices.getUserMedia(this.permission.constraints);
10895
+ disposeOfMediaStream(stream);
10896
+ return true;
10897
+ }
10898
+ catch (e) {
10899
+ if (e instanceof DOMException && e.name === 'NotAllowedError') {
10900
+ this.logger('info', 'Browser permission was not granted', {
10901
+ permission: this.permission,
10902
+ });
10903
+ if (throwOnNotAllowed) {
10904
+ throw e;
10905
+ }
10906
+ return false;
10907
+ }
10908
+ this.logger('error', `Failed to getUserMedia`, {
10909
+ error: e,
10910
+ permission: this.permission,
10911
+ });
10912
+ throw e;
10913
+ }
10914
+ });
10915
+ }
10916
+ listen(cb) {
10917
+ this.listeners.add(cb);
10918
+ if (this.state)
10919
+ cb(this.state);
10920
+ return () => this.listeners.delete(cb);
10921
+ }
10922
+ asObservable() {
10923
+ return rxjs.fromEventPattern((handler) => this.listen(handler), (handler, unlisten) => unlisten()).pipe(
10924
+ // In some browsers, the 'change' event doesn't reliably emit and hence,
10925
+ // permissionState stays in 'prompt' state forever.
10926
+ // Typically, this happens when a user grants one-time permission.
10927
+ // Instead of checking if a permission is granted, we check if it isn't denied
10928
+ rxjs.map((state) => state !== 'denied'));
10929
+ }
10930
+ setState(state) {
10931
+ if (this.state !== state) {
10932
+ this.state = state;
10933
+ this.listeners.forEach((listener) => listener(state));
10934
+ }
10935
+ }
10936
+ }
10937
+ function canQueryPermissions() {
10938
+ return (!isReactNative() &&
10939
+ typeof navigator !== 'undefined' &&
10940
+ !!navigator.permissions?.query);
10941
+ }
10942
+
10943
+ const uninitialized = Symbol('uninitialized');
10944
+ /**
10945
+ * Lazily creates a value using a provided factory
10946
+ */
10947
+ function lazy(factory) {
10948
+ let value = uninitialized;
10949
+ return () => {
10950
+ if (value === uninitialized) {
10951
+ value = factory();
10952
+ }
10953
+ return value;
10954
+ };
10955
+ }
10956
+
10831
10957
  /**
10832
10958
  * Returns an Observable that emits the list of available devices
10833
10959
  * that meet the given constraints.
@@ -10835,44 +10961,23 @@ const CallTypes = new CallTypesRegistry([
10835
10961
  * @param constraints the constraints to use when requesting the devices.
10836
10962
  * @param kind the kind of devices to enumerate.
10837
10963
  */
10838
- const getDevices = (constraints, kind) => {
10839
- return new rxjs.Observable((subscriber) => {
10840
- const enumerate = async () => {
10841
- let devices = await navigator.mediaDevices.enumerateDevices();
10842
- // some browsers report empty device labels (Firefox).
10843
- // in that case, we need to request permissions (via getUserMedia)
10844
- // to be able to get the device labels
10845
- const needsGetUserMedia = devices.some((device) => device.kind === kind && device.label === '');
10846
- if (needsGetUserMedia) {
10847
- let mediaStream;
10848
- try {
10849
- mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
10850
- devices = await navigator.mediaDevices.enumerateDevices();
10851
- }
10852
- finally {
10853
- if (mediaStream)
10854
- disposeOfMediaStream(mediaStream);
10855
- }
10856
- }
10857
- return devices;
10858
- };
10859
- enumerate()
10860
- .then((devices) => {
10861
- // notify subscribers and complete
10862
- subscriber.next(devices);
10863
- subscriber.complete();
10864
- })
10865
- .catch((error) => {
10866
- const logger = getLogger(['devices']);
10867
- logger('error', 'Failed to enumerate devices', error);
10868
- subscriber.error(error);
10869
- });
10870
- });
10964
+ const getDevices = (permission, kind) => {
10965
+ return rxjs.from((async () => {
10966
+ let devices = await navigator.mediaDevices.enumerateDevices();
10967
+ // for privacy reasons, most browsers don't give you device labels
10968
+ // unless you have a corresponding camera or microphone permission
10969
+ const shouldPromptForBrowserPermission = devices.some((device) => device.kind === kind && device.label === '');
10970
+ if (shouldPromptForBrowserPermission) {
10971
+ await permission.prompt({ throwOnNotAllowed: true });
10972
+ devices = await navigator.mediaDevices.enumerateDevices();
10973
+ }
10974
+ return devices.filter((d) => d.kind === kind);
10975
+ })());
10871
10976
  };
10872
10977
  /**
10873
- * [Tells if the browser supports audio output change on 'audio' elements](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId).
10874
- *
10875
- * */
10978
+ * Tells if the browser supports audio output change on 'audio' elements,
10979
+ * see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId.
10980
+ */
10876
10981
  const checkIfAudioOutputChangeSupported = () => {
10877
10982
  if (typeof document === 'undefined')
10878
10983
  return false;
@@ -10899,72 +11004,57 @@ const videoDeviceConstraints = {
10899
11004
  },
10900
11005
  };
10901
11006
  /**
10902
- * Creates a memoized observable instance
10903
- * that will be created only once and shared between all callers.
10904
- *
10905
- * @param create a function that creates an Observable.
10906
- */
10907
- const memoizedObservable = (create) => {
10908
- let memoized;
10909
- return () => {
10910
- if (!memoized)
10911
- memoized = create();
10912
- return memoized;
10913
- };
10914
- };
10915
- const getDeviceChangeObserver = memoizedObservable(() => {
10916
- // Audio and video devices are requested in two separate requests.
10917
- // That way, users will be presented with two separate prompts
10918
- // -> they can give access to just camera, or just microphone
10919
- return new rxjs.Observable((subscriber) => {
10920
- // 'addEventListener' is not available in React Native
10921
- if (!navigator.mediaDevices.addEventListener)
10922
- return;
10923
- const notify = () => subscriber.next();
10924
- navigator.mediaDevices.addEventListener('devicechange', notify);
10925
- return () => {
10926
- navigator.mediaDevices.removeEventListener('devicechange', notify);
10927
- };
10928
- }).pipe(rxjs.debounceTime(500), rxjs.concatMap(() => rxjs.from(navigator.mediaDevices.enumerateDevices())), rxjs.shareReplay(1));
10929
- });
10930
- const getAudioDevicesObserver = memoizedObservable(() => {
10931
- return rxjs.merge(getDevices(audioDeviceConstraints, 'audioinput'), getDeviceChangeObserver()).pipe(rxjs.shareReplay(1));
10932
- });
10933
- const getAudioOutputDevicesObserver = memoizedObservable(() => {
10934
- return rxjs.merge(getDevices(audioDeviceConstraints, 'audiooutput'), getDeviceChangeObserver()).pipe(rxjs.shareReplay(1));
10935
- });
10936
- const getVideoDevicesObserver = memoizedObservable(() => {
10937
- return rxjs.merge(getDevices(videoDeviceConstraints, 'videoinput'), getDeviceChangeObserver()).pipe(rxjs.shareReplay(1));
11007
+ * Keeps track of the browser permission to use microphone. This permission also
11008
+ * affects an ability to enumerate audio devices.
11009
+ */
11010
+ const getAudioBrowserPermission = lazy(() => new BrowserPermission({
11011
+ constraints: audioDeviceConstraints,
11012
+ queryName: 'microphone',
11013
+ }));
11014
+ /**
11015
+ * Keeps track of the browser permission to use camera. This permission also
11016
+ * affects an ability to enumerate video devices.
11017
+ */
11018
+ const getVideoBrowserPermission = lazy(() => new BrowserPermission({
11019
+ constraints: videoDeviceConstraints,
11020
+ queryName: 'camera',
11021
+ }));
11022
+ const getDeviceChangeObserver = lazy(() => {
11023
+ // 'addEventListener' is not available in React Native, returning
11024
+ // an observable that will never fire
11025
+ if (!navigator.mediaDevices.addEventListener)
11026
+ return rxjs.from([]);
11027
+ return rxjs.fromEvent(navigator.mediaDevices, 'devicechange').pipe(rxjs.map(() => undefined), rxjs.debounceTime(500));
10938
11028
  });
10939
11029
  /**
10940
- * Prompts the user for a permission to use audio devices (if not already granted) and lists the available 'audioinput' devices, if devices are added/removed the list is updated.
11030
+ * Prompts the user for a permission to use audio devices (if not already granted
11031
+ * and was not prompted before) and lists the available 'audioinput' devices,
11032
+ * if devices are added/removed the list is updated, and if the permission is revoked,
11033
+ * the observable errors.
10941
11034
  */
10942
- const getAudioDevices = () => {
10943
- return getAudioDevicesObserver().pipe(rxjs.map((values) => values.filter((d) => d.kind === 'audioinput')));
10944
- };
11035
+ const getAudioDevices = lazy(() => {
11036
+ return rxjs.merge(getDeviceChangeObserver(), getAudioBrowserPermission().asObservable()).pipe(rxjs.startWith(undefined), rxjs.concatMap(() => getDevices(getAudioBrowserPermission(), 'audioinput')), rxjs.shareReplay(1));
11037
+ });
10945
11038
  /**
10946
- * Prompts the user for a permission to use video devices (if not already granted) and lists the available 'videoinput' devices, if devices are added/removed the list is updated.
11039
+ * Prompts the user for a permission to use video devices (if not already granted
11040
+ * and was not prompted before) and lists the available 'videoinput' devices,
11041
+ * if devices are added/removed the list is updated, and if the permission is revoked,
11042
+ * the observable errors.
10947
11043
  */
10948
11044
  const getVideoDevices = () => {
10949
- return getVideoDevicesObserver().pipe(rxjs.map((values) => values.filter((d) => d.kind === 'videoinput')));
11045
+ return rxjs.merge(getDeviceChangeObserver(), getVideoBrowserPermission().asObservable()).pipe(rxjs.startWith(undefined), rxjs.concatMap(() => getDevices(getVideoBrowserPermission(), 'videoinput')), rxjs.shareReplay(1));
10950
11046
  };
10951
11047
  /**
10952
- * Prompts the user for a permission to use audio devices (if not already granted) and lists the available 'audiooutput' devices, if devices are added/removed the list is updated. Selecting 'audiooutput' device only makes sense if [the browser has support for changing audio output on 'audio' elements](#checkifaudiooutputchangesupported)
11048
+ * Prompts the user for a permission to use video devices (if not already granted
11049
+ * and was not prompted before) and lists the available 'audiooutput' devices,
11050
+ * if devices are added/removed the list is updated, and if the permission is revoked,
11051
+ * the observable errors.
10953
11052
  */
10954
11053
  const getAudioOutputDevices = () => {
10955
- return getAudioOutputDevicesObserver().pipe(rxjs.map((values) => values.filter((d) => d.kind === 'audiooutput')));
11054
+ return rxjs.merge(getDeviceChangeObserver(), getAudioBrowserPermission().asObservable()).pipe(rxjs.startWith(undefined), rxjs.concatMap(() => getDevices(getAudioBrowserPermission(), 'audiooutput')), rxjs.shareReplay(1));
10956
11055
  };
10957
11056
  const getStream = async (constraints) => {
10958
- try {
10959
- return await navigator.mediaDevices.getUserMedia(constraints);
10960
- }
10961
- catch (e) {
10962
- getLogger(['devices'])('error', `Failed to getUserMedia`, {
10963
- error: e,
10964
- constraints: constraints,
10965
- });
10966
- throw e;
10967
- }
11057
+ return await navigator.mediaDevices.getUserMedia(constraints);
10968
11058
  };
10969
11059
  /**
10970
11060
  * Returns an audio media stream that fulfills the given constraints.
@@ -10981,7 +11071,20 @@ const getAudioStream = async (trackConstraints) => {
10981
11071
  ...trackConstraints,
10982
11072
  },
10983
11073
  };
10984
- return getStream(constraints);
11074
+ try {
11075
+ await getAudioBrowserPermission().prompt({
11076
+ throwOnNotAllowed: true,
11077
+ forcePrompt: true,
11078
+ });
11079
+ return getStream(constraints);
11080
+ }
11081
+ catch (e) {
11082
+ getLogger(['devices'])('error', 'Failed to get audio stream', {
11083
+ error: e,
11084
+ constraints: constraints,
11085
+ });
11086
+ throw e;
11087
+ }
10985
11088
  };
10986
11089
  /**
10987
11090
  * Returns a video media stream that fulfills the given constraints.
@@ -10998,7 +11101,20 @@ const getVideoStream = async (trackConstraints) => {
10998
11101
  ...trackConstraints,
10999
11102
  },
11000
11103
  };
11001
- return getStream(constraints);
11104
+ try {
11105
+ await getVideoBrowserPermission().prompt({
11106
+ throwOnNotAllowed: true,
11107
+ forcePrompt: true,
11108
+ });
11109
+ return getStream(constraints);
11110
+ }
11111
+ catch (e) {
11112
+ getLogger(['devices'])('error', 'Failed to get video stream', {
11113
+ error: e,
11114
+ constraints: constraints,
11115
+ });
11116
+ throw e;
11117
+ }
11002
11118
  };
11003
11119
  /**
11004
11120
  * Prompts the user for a permission to share a screen.
@@ -11034,7 +11150,7 @@ const getScreenShareStream = async (options) => {
11034
11150
  };
11035
11151
  const deviceIds$ = typeof navigator !== 'undefined' &&
11036
11152
  typeof navigator.mediaDevices !== 'undefined'
11037
- ? memoizedObservable(() => rxjs.merge(rxjs.from(navigator.mediaDevices.enumerateDevices()), getDeviceChangeObserver()).pipe(rxjs.shareReplay(1)))()
11153
+ ? getDeviceChangeObserver().pipe(rxjs.startWith(undefined), rxjs.concatMap(() => navigator.mediaDevices.enumerateDevices()), rxjs.shareReplay(1))
11038
11154
  : undefined;
11039
11155
  /**
11040
11156
  * Deactivates MediaStream (stops and removes tracks) to be later garbage collected
@@ -11417,12 +11533,11 @@ class InputMediaDeviceManagerState {
11417
11533
  * Constructs new InputMediaDeviceManagerState instance.
11418
11534
  *
11419
11535
  * @param disableMode the disable mode to use.
11420
- * @param permissionName the permission name to use for querying.
11536
+ * @param permission the BrowserPermission to use for querying.
11421
11537
  * `undefined` means no permission is required.
11422
11538
  */
11423
- constructor(disableMode = 'stop-tracks', permissionName = undefined) {
11539
+ constructor(disableMode = 'stop-tracks', permission) {
11424
11540
  this.disableMode = disableMode;
11425
- this.permissionName = permissionName;
11426
11541
  this.statusSubject = new rxjs.BehaviorSubject(undefined);
11427
11542
  this.optimisticStatusSubject = new rxjs.BehaviorSubject(undefined);
11428
11543
  this.mediaStreamSubject = new rxjs.BehaviorSubject(undefined);
@@ -11453,43 +11568,6 @@ class InputMediaDeviceManagerState {
11453
11568
  * The default constraints for the device.
11454
11569
  */
11455
11570
  this.defaultConstraints$ = this.defaultConstraintsSubject.asObservable();
11456
- /**
11457
- * An observable that will emit `true` if browser/system permission
11458
- * is granted, `false` otherwise.
11459
- */
11460
- this.hasBrowserPermission$ = new rxjs.Observable((subscriber) => {
11461
- const notifyGranted = () => subscriber.next(true);
11462
- const permissionsAPIAvailable = !!navigator?.permissions?.query;
11463
- if (isReactNative() || !this.permissionName || !permissionsAPIAvailable) {
11464
- getLogger(['devices'])('warn', `Permissions can't be queried. Assuming granted.`);
11465
- return notifyGranted();
11466
- }
11467
- let permissionState;
11468
- const notify = () => {
11469
- subscriber.next(
11470
- // In some browsers, the 'change' event doesn't reliably emit and hence,
11471
- // permissionState stays in 'prompt' state forever.
11472
- // Typically, this happens when a user grants one-time permission.
11473
- // Instead of checking if a permission is granted, we check if it isn't denied
11474
- permissionState.state !== 'denied');
11475
- };
11476
- navigator.permissions
11477
- .query({ name: this.permissionName })
11478
- .then((permissionStatus) => {
11479
- permissionState = permissionStatus;
11480
- permissionState.addEventListener('change', notify);
11481
- notify();
11482
- })
11483
- .catch(() => {
11484
- // permission doesn't exist or can't be queried -> assume it's granted
11485
- // an example would be Firefox,
11486
- // where neither camera microphone permission can be queried
11487
- notifyGranted();
11488
- });
11489
- return () => {
11490
- permissionState?.removeEventListener('change', notify);
11491
- };
11492
- }).pipe(rxjs.shareReplay(1));
11493
11571
  /**
11494
11572
  * Gets the current value of an observable, or undefined if the observable has
11495
11573
  * not emitted a value yet.
@@ -11509,6 +11587,9 @@ class InputMediaDeviceManagerState {
11509
11587
  * @return the updated value.
11510
11588
  */
11511
11589
  this.setCurrentValue = setCurrentValue;
11590
+ this.hasBrowserPermission$ = permission
11591
+ ? permission.asObservable().pipe(rxjs.shareReplay(1))
11592
+ : rxjs.of(true);
11512
11593
  }
11513
11594
  /**
11514
11595
  * The device status
@@ -11588,10 +11669,7 @@ class InputMediaDeviceManagerState {
11588
11669
 
11589
11670
  class CameraManagerState extends InputMediaDeviceManagerState {
11590
11671
  constructor() {
11591
- super('stop-tracks',
11592
- // `camera` is not in the W3C standard yet,
11593
- // but it's supported by Chrome and Safari.
11594
- 'camera');
11672
+ super('stop-tracks', getVideoBrowserPermission());
11595
11673
  this.directionSubject = new rxjs.BehaviorSubject(undefined);
11596
11674
  this.direction$ = this.directionSubject
11597
11675
  .asObservable()
@@ -11722,10 +11800,7 @@ class CameraManager extends InputMediaDeviceManager {
11722
11800
 
11723
11801
  class MicrophoneManagerState extends InputMediaDeviceManagerState {
11724
11802
  constructor(disableMode) {
11725
- super(disableMode,
11726
- // `microphone` is not in the W3C standard yet,
11727
- // but it's supported by Chrome and Safari.
11728
- 'microphone');
11803
+ super(disableMode, getAudioBrowserPermission());
11729
11804
  this.speakingWhileMutedSubject = new rxjs.BehaviorSubject(false);
11730
11805
  this.speakingWhileMuted$ = this.speakingWhileMutedSubject
11731
11806
  .asObservable()
@@ -15428,7 +15503,7 @@ class StreamClient {
15428
15503
  });
15429
15504
  };
15430
15505
  this.getUserAgent = () => {
15431
- const version = "1.4.1" ;
15506
+ const version = "1.4.3" ;
15432
15507
  return (this.userAgent ||
15433
15508
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
15434
15509
  };
@@ -15960,6 +16035,7 @@ exports.descending = descending;
15960
16035
  exports.deviceIds$ = deviceIds$;
15961
16036
  exports.disposeOfMediaStream = disposeOfMediaStream;
15962
16037
  exports.dominantSpeaker = dominantSpeaker;
16038
+ exports.getAudioBrowserPermission = getAudioBrowserPermission;
15963
16039
  exports.getAudioDevices = getAudioDevices;
15964
16040
  exports.getAudioOutputDevices = getAudioOutputDevices;
15965
16041
  exports.getAudioStream = getAudioStream;
@@ -15969,6 +16045,7 @@ exports.getLogger = getLogger;
15969
16045
  exports.getOSInfo = getOSInfo;
15970
16046
  exports.getScreenShareStream = getScreenShareStream;
15971
16047
  exports.getSdkInfo = getSdkInfo;
16048
+ exports.getVideoBrowserPermission = getVideoBrowserPermission;
15972
16049
  exports.getVideoDevices = getVideoDevices;
15973
16050
  exports.getVideoStream = getVideoStream;
15974
16051
  exports.getWebRTCInfo = getWebRTCInfo;