@stream-io/video-client 1.4.2 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/index.browser.es.js +246 -154
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +246 -152
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +246 -154
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +7 -7
- package/dist/src/StreamSfuClient.d.ts +7 -7
- package/dist/src/StreamVideoClient.d.ts +5 -5
- package/dist/src/coordinator/connection/client.d.ts +13 -14
- package/dist/src/coordinator/connection/connection.d.ts +3 -5
- package/dist/src/coordinator/connection/insights.d.ts +0 -1
- package/dist/src/devices/BrowserPermission.d.ts +24 -0
- package/dist/src/devices/InputMediaDeviceManager.d.ts +4 -3
- package/dist/src/devices/InputMediaDeviceManagerState.d.ts +3 -3
- package/dist/src/devices/MicrophoneManager.d.ts +1 -0
- package/dist/src/devices/devices.d.ts +30 -11
- package/dist/src/devices/filters.d.ts +32 -0
- package/dist/src/helpers/ViewportTracker.d.ts +1 -1
- package/dist/src/helpers/lazy.d.ts +4 -0
- package/dist/src/helpers/sdp-munging.d.ts +2 -2
- package/dist/src/rtc/Dispatcher.d.ts +2 -2
- package/dist/src/rtc/codecs.d.ts +1 -1
- package/dist/src/rtc/signal.d.ts +0 -1
- package/dist/src/stats/utils.d.ts +4 -4
- package/dist/src/store/CallState.d.ts +1 -1
- package/package.json +4 -4
- package/src/devices/BrowserPermission.ts +152 -0
- package/src/devices/CameraManagerState.ts +2 -6
- package/src/devices/InputMediaDeviceManager.ts +55 -12
- package/src/devices/InputMediaDeviceManagerState.ts +10 -44
- package/src/devices/MicrophoneManager.ts +6 -4
- package/src/devices/MicrophoneManagerState.ts +2 -6
- package/src/devices/__tests__/CameraManager.test.ts +3 -0
- package/src/devices/__tests__/InputMediaDeviceManager.test.ts +5 -3
- package/src/devices/__tests__/InputMediaDeviceManagerFilters.test.ts +13 -8
- package/src/devices/__tests__/InputMediaDeviceManagerState.test.ts +41 -51
- package/src/devices/__tests__/MicrophoneManager.test.ts +4 -1
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +8 -1
- package/src/devices/__tests__/SpeakerManager.test.ts +8 -1
- package/src/devices/__tests__/mocks.ts +6 -1
- package/src/devices/devices.ts +113 -112
- package/src/devices/filters.ts +38 -0
- package/src/helpers/RNSpeechDetector.ts +1 -1
- package/src/helpers/lazy.ts +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
### [1.4.4](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.4.3...@stream-io/video-client-1.4.4) (2024-07-02)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* refactor background filters ([#1415](https://github.com/GetStream/stream-video-js/issues/1415)) ([deb6da2](https://github.com/GetStream/stream-video-js/commit/deb6da238f541c733451e84b198434671da8dceb))
|
|
11
|
+
|
|
12
|
+
### [1.4.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.4.2...@stream-io/video-client-1.4.3) (2024-06-25)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* improve browser permission handling ([#1394](https://github.com/GetStream/stream-video-js/issues/1394)) ([c8ccb21](https://github.com/GetStream/stream-video-js/commit/c8ccb219a43464d1215987d99fd01d8b4a407eb5))
|
|
18
|
+
|
|
5
19
|
### [1.4.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.4.1...@stream-io/video-client-1.4.2) (2024-06-24)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.browser.es.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ServiceType, stackIntercept } from '@protobuf-ts/runtime-rpc';
|
|
|
4
4
|
import axios, { AxiosHeaders } from 'axios';
|
|
5
5
|
export { AxiosError } from 'axios';
|
|
6
6
|
import { TwirpFetchTransport } from '@protobuf-ts/twirp-transport';
|
|
7
|
-
import { ReplaySubject, combineLatest, BehaviorSubject, map as map$1, shareReplay, distinctUntilChanged, takeWhile, distinctUntilKeyChanged, merge, from,
|
|
7
|
+
import { ReplaySubject, combineLatest, BehaviorSubject, map as map$1, shareReplay, distinctUntilChanged, takeWhile, distinctUntilKeyChanged, fromEventPattern, startWith, concatMap, merge, from, fromEvent, debounceTime, pairwise, of, filter, debounce, timer } from 'rxjs';
|
|
8
8
|
import * as SDP from 'sdp-transform';
|
|
9
9
|
import { UAParser } from 'ua-parser-js';
|
|
10
10
|
import WebSocket from 'isomorphic-ws';
|
|
@@ -10812,6 +10812,127 @@ const CallTypes = new CallTypesRegistry([
|
|
|
10812
10812
|
}),
|
|
10813
10813
|
]);
|
|
10814
10814
|
|
|
10815
|
+
class BrowserPermission {
|
|
10816
|
+
constructor(permission) {
|
|
10817
|
+
this.permission = permission;
|
|
10818
|
+
this.disposeController = new AbortController();
|
|
10819
|
+
this.wasPrompted = false;
|
|
10820
|
+
this.listeners = new Set();
|
|
10821
|
+
this.logger = getLogger(['permissions']);
|
|
10822
|
+
const signal = this.disposeController.signal;
|
|
10823
|
+
this.ready = (async () => {
|
|
10824
|
+
const assumeGranted = (error) => {
|
|
10825
|
+
this.logger('warn', "Can't query permissions, assuming granted", {
|
|
10826
|
+
permission,
|
|
10827
|
+
error,
|
|
10828
|
+
});
|
|
10829
|
+
this.setState('granted');
|
|
10830
|
+
};
|
|
10831
|
+
if (!canQueryPermissions()) {
|
|
10832
|
+
return assumeGranted();
|
|
10833
|
+
}
|
|
10834
|
+
try {
|
|
10835
|
+
const status = await navigator.permissions.query({
|
|
10836
|
+
name: permission.queryName,
|
|
10837
|
+
});
|
|
10838
|
+
if (!signal.aborted) {
|
|
10839
|
+
this.setState(status.state);
|
|
10840
|
+
status.addEventListener('change', () => this.setState(status.state), {
|
|
10841
|
+
signal,
|
|
10842
|
+
});
|
|
10843
|
+
}
|
|
10844
|
+
}
|
|
10845
|
+
catch (err) {
|
|
10846
|
+
assumeGranted(err);
|
|
10847
|
+
}
|
|
10848
|
+
})();
|
|
10849
|
+
}
|
|
10850
|
+
dispose() {
|
|
10851
|
+
this.state = undefined;
|
|
10852
|
+
this.disposeController.abort();
|
|
10853
|
+
}
|
|
10854
|
+
async getState() {
|
|
10855
|
+
await this.ready;
|
|
10856
|
+
if (!this.state) {
|
|
10857
|
+
throw new Error('BrowserPermission instance possibly disposed');
|
|
10858
|
+
}
|
|
10859
|
+
return this.state;
|
|
10860
|
+
}
|
|
10861
|
+
async prompt({ forcePrompt = false, throwOnNotAllowed = false, } = {}) {
|
|
10862
|
+
await withoutConcurrency(`permission-prompt-${this.permission.queryName}`, async () => {
|
|
10863
|
+
if ((await this.getState()) !== 'prompt' ||
|
|
10864
|
+
(this.wasPrompted && !forcePrompt)) {
|
|
10865
|
+
const isGranted = this.state === 'granted';
|
|
10866
|
+
if (!isGranted && throwOnNotAllowed) {
|
|
10867
|
+
throw new DOMException('Permission was not granted previously, and prompting again is not allowed', 'NotAllowedError');
|
|
10868
|
+
}
|
|
10869
|
+
return isGranted;
|
|
10870
|
+
}
|
|
10871
|
+
try {
|
|
10872
|
+
this.wasPrompted = true;
|
|
10873
|
+
const stream = await navigator.mediaDevices.getUserMedia(this.permission.constraints);
|
|
10874
|
+
disposeOfMediaStream(stream);
|
|
10875
|
+
return true;
|
|
10876
|
+
}
|
|
10877
|
+
catch (e) {
|
|
10878
|
+
if (e instanceof DOMException && e.name === 'NotAllowedError') {
|
|
10879
|
+
this.logger('info', 'Browser permission was not granted', {
|
|
10880
|
+
permission: this.permission,
|
|
10881
|
+
});
|
|
10882
|
+
if (throwOnNotAllowed) {
|
|
10883
|
+
throw e;
|
|
10884
|
+
}
|
|
10885
|
+
return false;
|
|
10886
|
+
}
|
|
10887
|
+
this.logger('error', `Failed to getUserMedia`, {
|
|
10888
|
+
error: e,
|
|
10889
|
+
permission: this.permission,
|
|
10890
|
+
});
|
|
10891
|
+
throw e;
|
|
10892
|
+
}
|
|
10893
|
+
});
|
|
10894
|
+
}
|
|
10895
|
+
listen(cb) {
|
|
10896
|
+
this.listeners.add(cb);
|
|
10897
|
+
if (this.state)
|
|
10898
|
+
cb(this.state);
|
|
10899
|
+
return () => this.listeners.delete(cb);
|
|
10900
|
+
}
|
|
10901
|
+
asObservable() {
|
|
10902
|
+
return fromEventPattern((handler) => this.listen(handler), (handler, unlisten) => unlisten()).pipe(
|
|
10903
|
+
// In some browsers, the 'change' event doesn't reliably emit and hence,
|
|
10904
|
+
// permissionState stays in 'prompt' state forever.
|
|
10905
|
+
// Typically, this happens when a user grants one-time permission.
|
|
10906
|
+
// Instead of checking if a permission is granted, we check if it isn't denied
|
|
10907
|
+
map$1((state) => state !== 'denied'));
|
|
10908
|
+
}
|
|
10909
|
+
setState(state) {
|
|
10910
|
+
if (this.state !== state) {
|
|
10911
|
+
this.state = state;
|
|
10912
|
+
this.listeners.forEach((listener) => listener(state));
|
|
10913
|
+
}
|
|
10914
|
+
}
|
|
10915
|
+
}
|
|
10916
|
+
function canQueryPermissions() {
|
|
10917
|
+
return (!isReactNative() &&
|
|
10918
|
+
typeof navigator !== 'undefined' &&
|
|
10919
|
+
!!navigator.permissions?.query);
|
|
10920
|
+
}
|
|
10921
|
+
|
|
10922
|
+
const uninitialized = Symbol('uninitialized');
|
|
10923
|
+
/**
|
|
10924
|
+
* Lazily creates a value using a provided factory
|
|
10925
|
+
*/
|
|
10926
|
+
function lazy(factory) {
|
|
10927
|
+
let value = uninitialized;
|
|
10928
|
+
return () => {
|
|
10929
|
+
if (value === uninitialized) {
|
|
10930
|
+
value = factory();
|
|
10931
|
+
}
|
|
10932
|
+
return value;
|
|
10933
|
+
};
|
|
10934
|
+
}
|
|
10935
|
+
|
|
10815
10936
|
/**
|
|
10816
10937
|
* Returns an Observable that emits the list of available devices
|
|
10817
10938
|
* that meet the given constraints.
|
|
@@ -10819,44 +10940,23 @@ const CallTypes = new CallTypesRegistry([
|
|
|
10819
10940
|
* @param constraints the constraints to use when requesting the devices.
|
|
10820
10941
|
* @param kind the kind of devices to enumerate.
|
|
10821
10942
|
*/
|
|
10822
|
-
const getDevices = (
|
|
10823
|
-
return
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
devices = await navigator.mediaDevices.enumerateDevices();
|
|
10835
|
-
}
|
|
10836
|
-
finally {
|
|
10837
|
-
if (mediaStream)
|
|
10838
|
-
disposeOfMediaStream(mediaStream);
|
|
10839
|
-
}
|
|
10840
|
-
}
|
|
10841
|
-
return devices;
|
|
10842
|
-
};
|
|
10843
|
-
enumerate()
|
|
10844
|
-
.then((devices) => {
|
|
10845
|
-
// notify subscribers and complete
|
|
10846
|
-
subscriber.next(devices);
|
|
10847
|
-
subscriber.complete();
|
|
10848
|
-
})
|
|
10849
|
-
.catch((error) => {
|
|
10850
|
-
const logger = getLogger(['devices']);
|
|
10851
|
-
logger('error', 'Failed to enumerate devices', error);
|
|
10852
|
-
subscriber.error(error);
|
|
10853
|
-
});
|
|
10854
|
-
});
|
|
10943
|
+
const getDevices = (permission, kind) => {
|
|
10944
|
+
return from((async () => {
|
|
10945
|
+
let devices = await navigator.mediaDevices.enumerateDevices();
|
|
10946
|
+
// for privacy reasons, most browsers don't give you device labels
|
|
10947
|
+
// unless you have a corresponding camera or microphone permission
|
|
10948
|
+
const shouldPromptForBrowserPermission = devices.some((device) => device.kind === kind && device.label === '');
|
|
10949
|
+
if (shouldPromptForBrowserPermission) {
|
|
10950
|
+
await permission.prompt({ throwOnNotAllowed: true });
|
|
10951
|
+
devices = await navigator.mediaDevices.enumerateDevices();
|
|
10952
|
+
}
|
|
10953
|
+
return devices.filter((d) => d.kind === kind);
|
|
10954
|
+
})());
|
|
10855
10955
|
};
|
|
10856
10956
|
/**
|
|
10857
|
-
*
|
|
10858
|
-
*
|
|
10859
|
-
|
|
10957
|
+
* Tells if the browser supports audio output change on 'audio' elements,
|
|
10958
|
+
* see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId.
|
|
10959
|
+
*/
|
|
10860
10960
|
const checkIfAudioOutputChangeSupported = () => {
|
|
10861
10961
|
if (typeof document === 'undefined')
|
|
10862
10962
|
return false;
|
|
@@ -10883,72 +10983,57 @@ const videoDeviceConstraints = {
|
|
|
10883
10983
|
},
|
|
10884
10984
|
};
|
|
10885
10985
|
/**
|
|
10886
|
-
*
|
|
10887
|
-
*
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
//
|
|
10903
|
-
|
|
10904
|
-
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
const notify = () => subscriber.next();
|
|
10908
|
-
navigator.mediaDevices.addEventListener('devicechange', notify);
|
|
10909
|
-
return () => {
|
|
10910
|
-
navigator.mediaDevices.removeEventListener('devicechange', notify);
|
|
10911
|
-
};
|
|
10912
|
-
}).pipe(debounceTime(500), concatMap(() => from(navigator.mediaDevices.enumerateDevices())), shareReplay(1));
|
|
10913
|
-
});
|
|
10914
|
-
const getAudioDevicesObserver = memoizedObservable(() => {
|
|
10915
|
-
return merge(getDevices(audioDeviceConstraints, 'audioinput'), getDeviceChangeObserver()).pipe(shareReplay(1));
|
|
10916
|
-
});
|
|
10917
|
-
const getAudioOutputDevicesObserver = memoizedObservable(() => {
|
|
10918
|
-
return merge(getDevices(audioDeviceConstraints, 'audiooutput'), getDeviceChangeObserver()).pipe(shareReplay(1));
|
|
10919
|
-
});
|
|
10920
|
-
const getVideoDevicesObserver = memoizedObservable(() => {
|
|
10921
|
-
return merge(getDevices(videoDeviceConstraints, 'videoinput'), getDeviceChangeObserver()).pipe(shareReplay(1));
|
|
10986
|
+
* Keeps track of the browser permission to use microphone. This permission also
|
|
10987
|
+
* affects an ability to enumerate audio devices.
|
|
10988
|
+
*/
|
|
10989
|
+
const getAudioBrowserPermission = lazy(() => new BrowserPermission({
|
|
10990
|
+
constraints: audioDeviceConstraints,
|
|
10991
|
+
queryName: 'microphone',
|
|
10992
|
+
}));
|
|
10993
|
+
/**
|
|
10994
|
+
* Keeps track of the browser permission to use camera. This permission also
|
|
10995
|
+
* affects an ability to enumerate video devices.
|
|
10996
|
+
*/
|
|
10997
|
+
const getVideoBrowserPermission = lazy(() => new BrowserPermission({
|
|
10998
|
+
constraints: videoDeviceConstraints,
|
|
10999
|
+
queryName: 'camera',
|
|
11000
|
+
}));
|
|
11001
|
+
const getDeviceChangeObserver = lazy(() => {
|
|
11002
|
+
// 'addEventListener' is not available in React Native, returning
|
|
11003
|
+
// an observable that will never fire
|
|
11004
|
+
if (!navigator.mediaDevices.addEventListener)
|
|
11005
|
+
return from([]);
|
|
11006
|
+
return fromEvent(navigator.mediaDevices, 'devicechange').pipe(map$1(() => undefined), debounceTime(500));
|
|
10922
11007
|
});
|
|
10923
11008
|
/**
|
|
10924
|
-
* Prompts the user for a permission to use audio devices (if not already granted
|
|
11009
|
+
* Prompts the user for a permission to use audio devices (if not already granted
|
|
11010
|
+
* and was not prompted before) and lists the available 'audioinput' devices,
|
|
11011
|
+
* if devices are added/removed the list is updated, and if the permission is revoked,
|
|
11012
|
+
* the observable errors.
|
|
10925
11013
|
*/
|
|
10926
|
-
const getAudioDevices = () => {
|
|
10927
|
-
return
|
|
10928
|
-
};
|
|
11014
|
+
const getAudioDevices = lazy(() => {
|
|
11015
|
+
return merge(getDeviceChangeObserver(), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audioinput')), shareReplay(1));
|
|
11016
|
+
});
|
|
10929
11017
|
/**
|
|
10930
|
-
* Prompts the user for a permission to use video devices (if not already granted
|
|
11018
|
+
* Prompts the user for a permission to use video devices (if not already granted
|
|
11019
|
+
* and was not prompted before) and lists the available 'videoinput' devices,
|
|
11020
|
+
* if devices are added/removed the list is updated, and if the permission is revoked,
|
|
11021
|
+
* the observable errors.
|
|
10931
11022
|
*/
|
|
10932
11023
|
const getVideoDevices = () => {
|
|
10933
|
-
return
|
|
11024
|
+
return merge(getDeviceChangeObserver(), getVideoBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getVideoBrowserPermission(), 'videoinput')), shareReplay(1));
|
|
10934
11025
|
};
|
|
10935
11026
|
/**
|
|
10936
|
-
* Prompts the user for a permission to use
|
|
11027
|
+
* Prompts the user for a permission to use video devices (if not already granted
|
|
11028
|
+
* and was not prompted before) and lists the available 'audiooutput' devices,
|
|
11029
|
+
* if devices are added/removed the list is updated, and if the permission is revoked,
|
|
11030
|
+
* the observable errors.
|
|
10937
11031
|
*/
|
|
10938
11032
|
const getAudioOutputDevices = () => {
|
|
10939
|
-
return
|
|
11033
|
+
return merge(getDeviceChangeObserver(), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audiooutput')), shareReplay(1));
|
|
10940
11034
|
};
|
|
10941
11035
|
const getStream = async (constraints) => {
|
|
10942
|
-
|
|
10943
|
-
return await navigator.mediaDevices.getUserMedia(constraints);
|
|
10944
|
-
}
|
|
10945
|
-
catch (e) {
|
|
10946
|
-
getLogger(['devices'])('error', `Failed to getUserMedia`, {
|
|
10947
|
-
error: e,
|
|
10948
|
-
constraints: constraints,
|
|
10949
|
-
});
|
|
10950
|
-
throw e;
|
|
10951
|
-
}
|
|
11036
|
+
return await navigator.mediaDevices.getUserMedia(constraints);
|
|
10952
11037
|
};
|
|
10953
11038
|
/**
|
|
10954
11039
|
* Returns an audio media stream that fulfills the given constraints.
|
|
@@ -10965,7 +11050,20 @@ const getAudioStream = async (trackConstraints) => {
|
|
|
10965
11050
|
...trackConstraints,
|
|
10966
11051
|
},
|
|
10967
11052
|
};
|
|
10968
|
-
|
|
11053
|
+
try {
|
|
11054
|
+
await getAudioBrowserPermission().prompt({
|
|
11055
|
+
throwOnNotAllowed: true,
|
|
11056
|
+
forcePrompt: true,
|
|
11057
|
+
});
|
|
11058
|
+
return getStream(constraints);
|
|
11059
|
+
}
|
|
11060
|
+
catch (e) {
|
|
11061
|
+
getLogger(['devices'])('error', 'Failed to get audio stream', {
|
|
11062
|
+
error: e,
|
|
11063
|
+
constraints: constraints,
|
|
11064
|
+
});
|
|
11065
|
+
throw e;
|
|
11066
|
+
}
|
|
10969
11067
|
};
|
|
10970
11068
|
/**
|
|
10971
11069
|
* Returns a video media stream that fulfills the given constraints.
|
|
@@ -10982,7 +11080,20 @@ const getVideoStream = async (trackConstraints) => {
|
|
|
10982
11080
|
...trackConstraints,
|
|
10983
11081
|
},
|
|
10984
11082
|
};
|
|
10985
|
-
|
|
11083
|
+
try {
|
|
11084
|
+
await getVideoBrowserPermission().prompt({
|
|
11085
|
+
throwOnNotAllowed: true,
|
|
11086
|
+
forcePrompt: true,
|
|
11087
|
+
});
|
|
11088
|
+
return getStream(constraints);
|
|
11089
|
+
}
|
|
11090
|
+
catch (e) {
|
|
11091
|
+
getLogger(['devices'])('error', 'Failed to get video stream', {
|
|
11092
|
+
error: e,
|
|
11093
|
+
constraints: constraints,
|
|
11094
|
+
});
|
|
11095
|
+
throw e;
|
|
11096
|
+
}
|
|
10986
11097
|
};
|
|
10987
11098
|
/**
|
|
10988
11099
|
* Prompts the user for a permission to share a screen.
|
|
@@ -11018,7 +11129,7 @@ const getScreenShareStream = async (options) => {
|
|
|
11018
11129
|
};
|
|
11019
11130
|
const deviceIds$ = typeof navigator !== 'undefined' &&
|
|
11020
11131
|
typeof navigator.mediaDevices !== 'undefined'
|
|
11021
|
-
?
|
|
11132
|
+
? getDeviceChangeObserver().pipe(startWith(undefined), concatMap(() => navigator.mediaDevices.enumerateDevices()), shareReplay(1))
|
|
11022
11133
|
: undefined;
|
|
11023
11134
|
/**
|
|
11024
11135
|
* Deactivates MediaStream (stops and removes tracks) to be later garbage collected
|
|
@@ -11053,6 +11164,7 @@ class InputMediaDeviceManager {
|
|
|
11053
11164
|
this.isTrackStoppedDueToTrackEnd = false;
|
|
11054
11165
|
this.filters = [];
|
|
11055
11166
|
this.statusChangeConcurrencyTag = Symbol('statusChangeConcurrencyTag');
|
|
11167
|
+
this.filterRegistrationConcurrencyTag = Symbol('filterRegistrationConcurrencyTag');
|
|
11056
11168
|
/**
|
|
11057
11169
|
* Disposes the manager.
|
|
11058
11170
|
*
|
|
@@ -11155,14 +11267,24 @@ class InputMediaDeviceManager {
|
|
|
11155
11267
|
* a new stream with the applied filter.
|
|
11156
11268
|
*
|
|
11157
11269
|
* @param filter the filter to register.
|
|
11158
|
-
* @returns
|
|
11270
|
+
* @returns MediaStreamFilterRegistrationResult
|
|
11159
11271
|
*/
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11272
|
+
registerFilter(filter) {
|
|
11273
|
+
const entry = {
|
|
11274
|
+
start: filter,
|
|
11275
|
+
stop: undefined,
|
|
11276
|
+
};
|
|
11277
|
+
const registered = withoutConcurrency(this.filterRegistrationConcurrencyTag, async () => {
|
|
11278
|
+
this.filters.push(entry);
|
|
11165
11279
|
await this.applySettingsToStream();
|
|
11280
|
+
});
|
|
11281
|
+
return {
|
|
11282
|
+
registered,
|
|
11283
|
+
unregister: () => withoutConcurrency(this.filterRegistrationConcurrencyTag, async () => {
|
|
11284
|
+
entry.stop?.();
|
|
11285
|
+
this.filters = this.filters.filter((f) => f !== entry);
|
|
11286
|
+
await this.applySettingsToStream();
|
|
11287
|
+
}),
|
|
11166
11288
|
};
|
|
11167
11289
|
}
|
|
11168
11290
|
/**
|
|
@@ -11215,6 +11337,7 @@ class InputMediaDeviceManager {
|
|
|
11215
11337
|
this.state.mediaStream.release();
|
|
11216
11338
|
}
|
|
11217
11339
|
this.state.setMediaStream(undefined, undefined);
|
|
11340
|
+
this.filters.forEach((entry) => entry.stop?.());
|
|
11218
11341
|
}
|
|
11219
11342
|
}
|
|
11220
11343
|
muteTracks() {
|
|
@@ -11319,7 +11442,16 @@ class InputMediaDeviceManager {
|
|
|
11319
11442
|
// e.g. camera or microphone stream
|
|
11320
11443
|
rootStream = this.getStream(constraints);
|
|
11321
11444
|
// we publish the last MediaStream of the chain
|
|
11322
|
-
stream = await this.filters.reduce((parent,
|
|
11445
|
+
stream = await this.filters.reduce((parent, entry) => parent
|
|
11446
|
+
.then((inputStream) => {
|
|
11447
|
+
const { stop, output } = entry.start(inputStream);
|
|
11448
|
+
entry.stop = stop;
|
|
11449
|
+
return output;
|
|
11450
|
+
})
|
|
11451
|
+
.then(chainWith(parent), (error) => {
|
|
11452
|
+
this.logger('warn', 'Fitler failed to start and will be ignored', error);
|
|
11453
|
+
return parent;
|
|
11454
|
+
}), rootStream);
|
|
11323
11455
|
}
|
|
11324
11456
|
if (this.call.state.callingState === CallingState.JOINED) {
|
|
11325
11457
|
await this.publishStream(stream);
|
|
@@ -11401,12 +11533,11 @@ class InputMediaDeviceManagerState {
|
|
|
11401
11533
|
* Constructs new InputMediaDeviceManagerState instance.
|
|
11402
11534
|
*
|
|
11403
11535
|
* @param disableMode the disable mode to use.
|
|
11404
|
-
* @param
|
|
11536
|
+
* @param permission the BrowserPermission to use for querying.
|
|
11405
11537
|
* `undefined` means no permission is required.
|
|
11406
11538
|
*/
|
|
11407
|
-
constructor(disableMode = 'stop-tracks',
|
|
11539
|
+
constructor(disableMode = 'stop-tracks', permission) {
|
|
11408
11540
|
this.disableMode = disableMode;
|
|
11409
|
-
this.permissionName = permissionName;
|
|
11410
11541
|
this.statusSubject = new BehaviorSubject(undefined);
|
|
11411
11542
|
this.optimisticStatusSubject = new BehaviorSubject(undefined);
|
|
11412
11543
|
this.mediaStreamSubject = new BehaviorSubject(undefined);
|
|
@@ -11437,43 +11568,6 @@ class InputMediaDeviceManagerState {
|
|
|
11437
11568
|
* The default constraints for the device.
|
|
11438
11569
|
*/
|
|
11439
11570
|
this.defaultConstraints$ = this.defaultConstraintsSubject.asObservable();
|
|
11440
|
-
/**
|
|
11441
|
-
* An observable that will emit `true` if browser/system permission
|
|
11442
|
-
* is granted, `false` otherwise.
|
|
11443
|
-
*/
|
|
11444
|
-
this.hasBrowserPermission$ = new Observable((subscriber) => {
|
|
11445
|
-
const notifyGranted = () => subscriber.next(true);
|
|
11446
|
-
const permissionsAPIAvailable = !!navigator?.permissions?.query;
|
|
11447
|
-
if (isReactNative() || !this.permissionName || !permissionsAPIAvailable) {
|
|
11448
|
-
getLogger(['devices'])('warn', `Permissions can't be queried. Assuming granted.`);
|
|
11449
|
-
return notifyGranted();
|
|
11450
|
-
}
|
|
11451
|
-
let permissionState;
|
|
11452
|
-
const notify = () => {
|
|
11453
|
-
subscriber.next(
|
|
11454
|
-
// In some browsers, the 'change' event doesn't reliably emit and hence,
|
|
11455
|
-
// permissionState stays in 'prompt' state forever.
|
|
11456
|
-
// Typically, this happens when a user grants one-time permission.
|
|
11457
|
-
// Instead of checking if a permission is granted, we check if it isn't denied
|
|
11458
|
-
permissionState.state !== 'denied');
|
|
11459
|
-
};
|
|
11460
|
-
navigator.permissions
|
|
11461
|
-
.query({ name: this.permissionName })
|
|
11462
|
-
.then((permissionStatus) => {
|
|
11463
|
-
permissionState = permissionStatus;
|
|
11464
|
-
permissionState.addEventListener('change', notify);
|
|
11465
|
-
notify();
|
|
11466
|
-
})
|
|
11467
|
-
.catch(() => {
|
|
11468
|
-
// permission doesn't exist or can't be queried -> assume it's granted
|
|
11469
|
-
// an example would be Firefox,
|
|
11470
|
-
// where neither camera microphone permission can be queried
|
|
11471
|
-
notifyGranted();
|
|
11472
|
-
});
|
|
11473
|
-
return () => {
|
|
11474
|
-
permissionState?.removeEventListener('change', notify);
|
|
11475
|
-
};
|
|
11476
|
-
}).pipe(shareReplay(1));
|
|
11477
11571
|
/**
|
|
11478
11572
|
* Gets the current value of an observable, or undefined if the observable has
|
|
11479
11573
|
* not emitted a value yet.
|
|
@@ -11493,6 +11587,9 @@ class InputMediaDeviceManagerState {
|
|
|
11493
11587
|
* @return the updated value.
|
|
11494
11588
|
*/
|
|
11495
11589
|
this.setCurrentValue = setCurrentValue;
|
|
11590
|
+
this.hasBrowserPermission$ = permission
|
|
11591
|
+
? permission.asObservable().pipe(shareReplay(1))
|
|
11592
|
+
: of(true);
|
|
11496
11593
|
}
|
|
11497
11594
|
/**
|
|
11498
11595
|
* The device status
|
|
@@ -11572,10 +11669,7 @@ class InputMediaDeviceManagerState {
|
|
|
11572
11669
|
|
|
11573
11670
|
class CameraManagerState extends InputMediaDeviceManagerState {
|
|
11574
11671
|
constructor() {
|
|
11575
|
-
super('stop-tracks',
|
|
11576
|
-
// `camera` is not in the W3C standard yet,
|
|
11577
|
-
// but it's supported by Chrome and Safari.
|
|
11578
|
-
'camera');
|
|
11672
|
+
super('stop-tracks', getVideoBrowserPermission());
|
|
11579
11673
|
this.directionSubject = new BehaviorSubject(undefined);
|
|
11580
11674
|
this.direction$ = this.directionSubject
|
|
11581
11675
|
.asObservable()
|
|
@@ -11706,10 +11800,7 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
11706
11800
|
|
|
11707
11801
|
class MicrophoneManagerState extends InputMediaDeviceManagerState {
|
|
11708
11802
|
constructor(disableMode) {
|
|
11709
|
-
super(disableMode,
|
|
11710
|
-
// `microphone` is not in the W3C standard yet,
|
|
11711
|
-
// but it's supported by Chrome and Safari.
|
|
11712
|
-
'microphone');
|
|
11803
|
+
super(disableMode, getAudioBrowserPermission());
|
|
11713
11804
|
this.speakingWhileMutedSubject = new BehaviorSubject(false);
|
|
11714
11805
|
this.speakingWhileMuted$ = this.speakingWhileMutedSubject
|
|
11715
11806
|
.asObservable()
|
|
@@ -11965,7 +12056,9 @@ class MicrophoneManager extends InputMediaDeviceManager {
|
|
|
11965
12056
|
});
|
|
11966
12057
|
}
|
|
11967
12058
|
});
|
|
11968
|
-
|
|
12059
|
+
const registrationResult = this.registerFilter(noiseCancellation.toFilter());
|
|
12060
|
+
this.noiseCancellationRegistration = registrationResult.registered;
|
|
12061
|
+
this.uregisterNoiseCancellation = registrationResult.unregister;
|
|
11969
12062
|
await this.noiseCancellationRegistration;
|
|
11970
12063
|
// handles an edge case where a noise cancellation is enabled after
|
|
11971
12064
|
// the participant as joined the call -> we immediately enable NC
|
|
@@ -11991,8 +12084,7 @@ class MicrophoneManager extends InputMediaDeviceManager {
|
|
|
11991
12084
|
if (isReactNative()) {
|
|
11992
12085
|
throw new Error('Noise cancellation is not supported in React Native');
|
|
11993
12086
|
}
|
|
11994
|
-
await this.
|
|
11995
|
-
?.then((unregister) => unregister())
|
|
12087
|
+
await (this.uregisterNoiseCancellation?.() ?? Promise.resolve())
|
|
11996
12088
|
.then(() => this.noiseCancellation?.disable())
|
|
11997
12089
|
.then(() => this.noiseCancellationChangeUnsubscribe?.())
|
|
11998
12090
|
.catch((err) => {
|
|
@@ -15414,7 +15506,7 @@ class StreamClient {
|
|
|
15414
15506
|
});
|
|
15415
15507
|
};
|
|
15416
15508
|
this.getUserAgent = () => {
|
|
15417
|
-
const version = "1.4.
|
|
15509
|
+
const version = "1.4.4" ;
|
|
15418
15510
|
return (this.userAgent ||
|
|
15419
15511
|
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
|
|
15420
15512
|
};
|
|
@@ -15893,5 +15985,5 @@ class StreamVideoClient {
|
|
|
15893
15985
|
}
|
|
15894
15986
|
}
|
|
15895
15987
|
|
|
15896
|
-
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, BlockListOptionsBehaviorEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, ChannelConfigWithInfoAutomodBehaviorEnum, ChannelConfigWithInfoAutomodEnum, ChannelConfigWithInfoBlocklistBehaviorEnum, CreateDeviceRequestPushProviderEnum, DebounceType, DynascaleManager, ErrorFromResponse, InputMediaDeviceManager, InputMediaDeviceManagerState, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, models as SfuModels, SpeakerManager, SpeakerState, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceInfo, getLogger, getOSInfo, getScreenShareStream, getSdkInfo, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio, hasScreenShare, hasScreenShareAudio, hasVideo, isPinned, livestreamOrAudioRoomSortPreset, logLevels, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, role, screenSharing, setDeviceInfo, setLogLevel, setLogger, setOSInfo, setSdkInfo, setWebRTCInfo, speakerLayoutSortPreset, speaking };
|
|
15988
|
+
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, BlockListOptionsBehaviorEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, ChannelConfigWithInfoAutomodBehaviorEnum, ChannelConfigWithInfoAutomodEnum, ChannelConfigWithInfoBlocklistBehaviorEnum, CreateDeviceRequestPushProviderEnum, DebounceType, DynascaleManager, ErrorFromResponse, InputMediaDeviceManager, InputMediaDeviceManagerState, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, models as SfuModels, SpeakerManager, SpeakerState, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceInfo, getLogger, getOSInfo, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio, hasScreenShare, hasScreenShareAudio, hasVideo, isPinned, livestreamOrAudioRoomSortPreset, logLevels, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, role, screenSharing, setDeviceInfo, setLogLevel, setLogger, setOSInfo, setSdkInfo, setWebRTCInfo, speakerLayoutSortPreset, speaking };
|
|
15897
15989
|
//# sourceMappingURL=index.browser.es.js.map
|