@stream-io/video-client 1.27.0 → 1.27.2
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 +94 -85
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +93 -84
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +94 -85
- package/dist/index.es.js.map +1 -1
- package/dist/src/coordinator/connection/client.d.ts +1 -5
- package/dist/src/devices/devices.d.ts +5 -12
- package/dist/src/devices/index.d.ts +1 -0
- package/dist/src/devices/utils.d.ts +7 -0
- package/dist/src/helpers/lazy.d.ts +1 -1
- package/dist/src/stats/rtc/Tracer.d.ts +4 -1
- package/dist/src/stats/rtc/types.d.ts +1 -0
- package/dist/src/timers/index.d.ts +1 -1
- package/package.json +2 -2
- package/src/Call.ts +13 -4
- package/src/StreamSfuClient.ts +3 -2
- package/src/coordinator/connection/client.ts +27 -46
- package/src/coordinator/connection/connection.ts +0 -1
- package/src/devices/BrowserPermission.ts +1 -1
- package/src/devices/CameraManager.ts +1 -1
- package/src/devices/InputMediaDeviceManager.ts +5 -3
- package/src/devices/MicrophoneManager.ts +2 -1
- package/src/devices/SpeakerManager.ts +1 -1
- package/src/devices/devices.ts +29 -29
- package/src/devices/index.ts +1 -0
- package/src/devices/utils.ts +17 -0
- package/src/helpers/lazy.ts +3 -3
- package/src/rtc/__tests__/videoLayers.test.ts +4 -6
- package/src/rtc/videoLayers.ts +12 -6
- package/src/stats/rtc/Tracer.ts +19 -1
- package/src/stats/rtc/types.ts +1 -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.27.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.27.1...@stream-io/video-client-1.27.2) (2025-08-05)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- improved logging and tracing ([#1874](https://github.com/GetStream/stream-video-js/issues/1874)) ([e450ce2](https://github.com/GetStream/stream-video-js/commit/e450ce2a294d6f80480fcc709591c13d9ede79e4))
|
|
10
|
+
|
|
11
|
+
## [1.27.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.27.0...@stream-io/video-client-1.27.1) (2025-07-25)
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
- improved audio and video filter tracing ([#1862](https://github.com/GetStream/stream-video-js/issues/1862)) ([701ea4b](https://github.com/GetStream/stream-video-js/commit/701ea4b3266f68072c1325b70221fdefd77137ec))
|
|
16
|
+
- synchronize updateMuteState; use correct fallback dimensions ([#1867](https://github.com/GetStream/stream-video-js/issues/1867)) ([154cdda](https://github.com/GetStream/stream-video-js/commit/154cddaa4462ee03af5fdf4929ad9f4e3d4b5070))
|
|
17
|
+
- trace available devices and thermal state changes ([#1866](https://github.com/GetStream/stream-video-js/issues/1866)) ([d8312b5](https://github.com/GetStream/stream-video-js/commit/d8312b5c109b14baa28ee764202d387499d0fd52))
|
|
18
|
+
|
|
5
19
|
## [1.27.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.26.1...@stream-io/video-client-1.27.0) (2025-07-18)
|
|
6
20
|
|
|
7
21
|
### Features
|
package/dist/index.browser.es.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ServiceType, stackIntercept, RpcError } from '@protobuf-ts/runtime-rpc'
|
|
|
4
4
|
import axios from 'axios';
|
|
5
5
|
export { AxiosError } from 'axios';
|
|
6
6
|
import { TwirpFetchTransport, TwirpErrorCode } from '@protobuf-ts/twirp-transport';
|
|
7
|
-
import { ReplaySubject, combineLatest, BehaviorSubject, shareReplay, map, distinctUntilChanged, takeWhile, distinctUntilKeyChanged, fromEventPattern, startWith, concatMap, merge, from, fromEvent, debounceTime, pairwise, of } from 'rxjs';
|
|
7
|
+
import { ReplaySubject, combineLatest, BehaviorSubject, shareReplay, map, distinctUntilChanged, takeWhile, distinctUntilKeyChanged, fromEventPattern, startWith, concatMap, merge, from, fromEvent, tap, debounceTime, pairwise, of } from 'rxjs';
|
|
8
8
|
import { UAParser } from 'ua-parser-js';
|
|
9
9
|
import { parse, write } from 'sdp-transform';
|
|
10
10
|
|
|
@@ -5914,7 +5914,7 @@ const aggregate = (stats) => {
|
|
|
5914
5914
|
return report;
|
|
5915
5915
|
};
|
|
5916
5916
|
|
|
5917
|
-
const version = "1.27.
|
|
5917
|
+
const version = "1.27.2";
|
|
5918
5918
|
const [major, minor, patch] = version.split('.');
|
|
5919
5919
|
let sdkInfo = {
|
|
5920
5920
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -6498,6 +6498,15 @@ class Tracer {
|
|
|
6498
6498
|
return;
|
|
6499
6499
|
this.buffer.push([tag, this.id, data, Date.now()]);
|
|
6500
6500
|
};
|
|
6501
|
+
this.traceOnce = (key, tag, data) => {
|
|
6502
|
+
if (this.keys?.has(key))
|
|
6503
|
+
return;
|
|
6504
|
+
this.trace(tag, data);
|
|
6505
|
+
(this.keys ?? (this.keys = new Map())).set(key, true);
|
|
6506
|
+
};
|
|
6507
|
+
this.resetTrace = (key) => {
|
|
6508
|
+
this.keys?.delete(key);
|
|
6509
|
+
};
|
|
6501
6510
|
this.take = () => {
|
|
6502
6511
|
const snapshot = this.buffer;
|
|
6503
6512
|
this.buffer = [];
|
|
@@ -6510,6 +6519,7 @@ class Tracer {
|
|
|
6510
6519
|
};
|
|
6511
6520
|
this.dispose = () => {
|
|
6512
6521
|
this.buffer = [];
|
|
6522
|
+
this.keys?.clear();
|
|
6513
6523
|
};
|
|
6514
6524
|
this.id = id;
|
|
6515
6525
|
}
|
|
@@ -6973,9 +6983,8 @@ const computeVideoLayers = (videoTrack, publishOption) => {
|
|
|
6973
6983
|
if (isAudioTrackType(publishOption.trackType))
|
|
6974
6984
|
return;
|
|
6975
6985
|
const optimalVideoLayers = [];
|
|
6976
|
-
const
|
|
6977
|
-
const { width =
|
|
6978
|
-
const { bitrate, codec, fps, maxSpatialLayers = 3, maxTemporalLayers = 3, videoDimension = { width: 1280, height: 720 }, useSingleLayer, } = publishOption;
|
|
6986
|
+
const { bitrate, codec, fps = 30, maxSpatialLayers = 3, maxTemporalLayers = 3, videoDimension = { width: 1280, height: 720 }, useSingleLayer, } = publishOption;
|
|
6987
|
+
const { width = videoDimension.width, height = videoDimension.height } = videoTrack.getSettings();
|
|
6979
6988
|
const maxBitrate = getComputedMaxBitrate(videoDimension, width, height, bitrate);
|
|
6980
6989
|
let downscaleFactor = 1;
|
|
6981
6990
|
let bitrateFactor = 1;
|
|
@@ -7007,7 +7016,7 @@ const computeVideoLayers = (videoTrack, publishOption) => {
|
|
|
7007
7016
|
}
|
|
7008
7017
|
// for simplicity, we start with all layers enabled, then this function
|
|
7009
7018
|
// will clear/reassign the layers that are not needed
|
|
7010
|
-
return withSimulcastConstraints(
|
|
7019
|
+
return withSimulcastConstraints(width, height, optimalVideoLayers, useSingleLayer);
|
|
7011
7020
|
};
|
|
7012
7021
|
/**
|
|
7013
7022
|
* Computes the maximum bitrate for a given resolution.
|
|
@@ -7041,9 +7050,9 @@ const getComputedMaxBitrate = (targetResolution, currentWidth, currentHeight, bi
|
|
|
7041
7050
|
*
|
|
7042
7051
|
* https://chromium.googlesource.com/external/webrtc/+/refs/heads/main/media/engine/simulcast.cc#90
|
|
7043
7052
|
*/
|
|
7044
|
-
const withSimulcastConstraints = (
|
|
7053
|
+
const withSimulcastConstraints = (width, height, optimalVideoLayers, useSingleLayer) => {
|
|
7045
7054
|
let layers;
|
|
7046
|
-
const size = Math.max(
|
|
7055
|
+
const size = Math.max(width, height);
|
|
7047
7056
|
if (size <= 320) {
|
|
7048
7057
|
// provide only one layer 320x240 (f), the one with the highest quality
|
|
7049
7058
|
layers = optimalVideoLayers.filter((layer) => layer.rid === 'f');
|
|
@@ -7727,9 +7736,9 @@ const uninitialized = Symbol('uninitialized');
|
|
|
7727
7736
|
*/
|
|
7728
7737
|
function lazy(factory) {
|
|
7729
7738
|
let value = uninitialized;
|
|
7730
|
-
return () => {
|
|
7739
|
+
return (...args) => {
|
|
7731
7740
|
if (value === uninitialized) {
|
|
7732
|
-
value = factory();
|
|
7741
|
+
value = factory(...args);
|
|
7733
7742
|
}
|
|
7734
7743
|
return value;
|
|
7735
7744
|
};
|
|
@@ -8060,7 +8069,6 @@ class StreamSfuClient {
|
|
|
8060
8069
|
const current = this.joinResponseTask;
|
|
8061
8070
|
let timeoutId = undefined;
|
|
8062
8071
|
const unsubscribe = this.dispatcher.on('joinResponse', (joinResponse) => {
|
|
8063
|
-
this.logger('debug', 'Received joinResponse', joinResponse);
|
|
8064
8072
|
clearTimeout(timeoutId);
|
|
8065
8073
|
unsubscribe();
|
|
8066
8074
|
this.keepAlive();
|
|
@@ -8068,7 +8076,9 @@ class StreamSfuClient {
|
|
|
8068
8076
|
});
|
|
8069
8077
|
timeoutId = setTimeout(() => {
|
|
8070
8078
|
unsubscribe();
|
|
8071
|
-
|
|
8079
|
+
const message = `Waiting for "joinResponse" has timed out after ${this.joinResponseTimeout}ms`;
|
|
8080
|
+
this.tracer?.trace('joinRequestTimeout', message);
|
|
8081
|
+
current.reject(new Error(message));
|
|
8072
8082
|
}, this.joinResponseTimeout);
|
|
8073
8083
|
const joinRequest = SfuRequest.create({
|
|
8074
8084
|
requestPayload: {
|
|
@@ -9412,6 +9422,25 @@ const CallTypes = new CallTypesRegistry([
|
|
|
9412
9422
|
}),
|
|
9413
9423
|
]);
|
|
9414
9424
|
|
|
9425
|
+
/**
|
|
9426
|
+
* Deactivates MediaStream (stops and removes tracks) to be later garbage collected
|
|
9427
|
+
*
|
|
9428
|
+
* @param stream MediaStream
|
|
9429
|
+
* @returns void
|
|
9430
|
+
*/
|
|
9431
|
+
const disposeOfMediaStream = (stream) => {
|
|
9432
|
+
if (!stream.active)
|
|
9433
|
+
return;
|
|
9434
|
+
stream.getTracks().forEach((track) => {
|
|
9435
|
+
track.stop();
|
|
9436
|
+
});
|
|
9437
|
+
// @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
|
|
9438
|
+
if (typeof stream.release === 'function') {
|
|
9439
|
+
// @ts-expect-error - release() is present in react-native-webrtc
|
|
9440
|
+
stream.release();
|
|
9441
|
+
}
|
|
9442
|
+
};
|
|
9443
|
+
|
|
9415
9444
|
class BrowserPermission {
|
|
9416
9445
|
constructor(permission) {
|
|
9417
9446
|
this.permission = permission;
|
|
@@ -9543,8 +9572,9 @@ function canQueryPermissions() {
|
|
|
9543
9572
|
*
|
|
9544
9573
|
* @param permission a BrowserPermission instance.
|
|
9545
9574
|
* @param kind the kind of devices to enumerate.
|
|
9575
|
+
* @param tracer the tracer to use for tracing the device enumeration.
|
|
9546
9576
|
*/
|
|
9547
|
-
const getDevices = (permission, kind) => {
|
|
9577
|
+
const getDevices = (permission, kind, tracer) => {
|
|
9548
9578
|
return from((async () => {
|
|
9549
9579
|
let devices = await navigator.mediaDevices.enumerateDevices();
|
|
9550
9580
|
// for privacy reasons, most browsers don't give you device labels
|
|
@@ -9553,6 +9583,7 @@ const getDevices = (permission, kind) => {
|
|
|
9553
9583
|
if (shouldPromptForBrowserPermission && (await permission.prompt())) {
|
|
9554
9584
|
devices = await navigator.mediaDevices.enumerateDevices();
|
|
9555
9585
|
}
|
|
9586
|
+
tracer?.traceOnce('device-enumeration', 'navigator.mediaDevices.enumerateDevices', devices);
|
|
9556
9587
|
return devices.filter((device) => device.kind === kind &&
|
|
9557
9588
|
device.label !== '' &&
|
|
9558
9589
|
device.deviceId !== 'default');
|
|
@@ -9603,12 +9634,12 @@ const getVideoBrowserPermission = lazy(() => new BrowserPermission({
|
|
|
9603
9634
|
constraints: videoDeviceConstraints,
|
|
9604
9635
|
queryName: 'camera',
|
|
9605
9636
|
}));
|
|
9606
|
-
const getDeviceChangeObserver = lazy(() => {
|
|
9637
|
+
const getDeviceChangeObserver = lazy((tracer) => {
|
|
9607
9638
|
// 'addEventListener' is not available in React Native, returning
|
|
9608
9639
|
// an observable that will never fire
|
|
9609
9640
|
if (!navigator.mediaDevices.addEventListener)
|
|
9610
9641
|
return from([]);
|
|
9611
|
-
return fromEvent(navigator.mediaDevices, 'devicechange').pipe(map(() => undefined), debounceTime(500));
|
|
9642
|
+
return fromEvent(navigator.mediaDevices, 'devicechange').pipe(tap(() => tracer?.resetTrace('device-enumeration')), map(() => undefined), debounceTime(500));
|
|
9612
9643
|
});
|
|
9613
9644
|
/**
|
|
9614
9645
|
* Prompts the user for a permission to use audio devices (if not already granted
|
|
@@ -9616,8 +9647,8 @@ const getDeviceChangeObserver = lazy(() => {
|
|
|
9616
9647
|
* if devices are added/removed the list is updated, and if the permission is revoked,
|
|
9617
9648
|
* the observable errors.
|
|
9618
9649
|
*/
|
|
9619
|
-
const getAudioDevices = lazy(() => {
|
|
9620
|
-
return merge(getDeviceChangeObserver(), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audioinput')), shareReplay(1));
|
|
9650
|
+
const getAudioDevices = lazy((tracer) => {
|
|
9651
|
+
return merge(getDeviceChangeObserver(tracer), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audioinput', tracer)), shareReplay(1));
|
|
9621
9652
|
});
|
|
9622
9653
|
/**
|
|
9623
9654
|
* Prompts the user for a permission to use video devices (if not already granted
|
|
@@ -9625,8 +9656,8 @@ const getAudioDevices = lazy(() => {
|
|
|
9625
9656
|
* if devices are added/removed the list is updated, and if the permission is revoked,
|
|
9626
9657
|
* the observable errors.
|
|
9627
9658
|
*/
|
|
9628
|
-
const getVideoDevices = lazy(() => {
|
|
9629
|
-
return merge(getDeviceChangeObserver(), getVideoBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getVideoBrowserPermission(), 'videoinput')), shareReplay(1));
|
|
9659
|
+
const getVideoDevices = lazy((tracer) => {
|
|
9660
|
+
return merge(getDeviceChangeObserver(tracer), getVideoBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getVideoBrowserPermission(), 'videoinput', tracer)), shareReplay(1));
|
|
9630
9661
|
});
|
|
9631
9662
|
/**
|
|
9632
9663
|
* Prompts the user for a permission to use video devices (if not already granted
|
|
@@ -9634,8 +9665,8 @@ const getVideoDevices = lazy(() => {
|
|
|
9634
9665
|
* if devices are added/removed the list is updated, and if the permission is revoked,
|
|
9635
9666
|
* the observable errors.
|
|
9636
9667
|
*/
|
|
9637
|
-
const getAudioOutputDevices = lazy(() => {
|
|
9638
|
-
return merge(getDeviceChangeObserver(), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audiooutput')), shareReplay(1));
|
|
9668
|
+
const getAudioOutputDevices = lazy((tracer) => {
|
|
9669
|
+
return merge(getDeviceChangeObserver(tracer), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audiooutput', tracer)), shareReplay(1));
|
|
9639
9670
|
});
|
|
9640
9671
|
let getUserMediaExecId = 0;
|
|
9641
9672
|
const getStream = async (constraints, tracer) => {
|
|
@@ -9807,24 +9838,6 @@ const deviceIds$ = typeof navigator !== 'undefined' &&
|
|
|
9807
9838
|
typeof navigator.mediaDevices !== 'undefined'
|
|
9808
9839
|
? getDeviceChangeObserver().pipe(startWith(undefined), concatMap(() => navigator.mediaDevices.enumerateDevices()), shareReplay(1))
|
|
9809
9840
|
: undefined;
|
|
9810
|
-
/**
|
|
9811
|
-
* Deactivates MediaStream (stops and removes tracks) to be later garbage collected
|
|
9812
|
-
*
|
|
9813
|
-
* @param stream MediaStream
|
|
9814
|
-
* @returns void
|
|
9815
|
-
*/
|
|
9816
|
-
const disposeOfMediaStream = (stream) => {
|
|
9817
|
-
if (!stream.active)
|
|
9818
|
-
return;
|
|
9819
|
-
stream.getTracks().forEach((track) => {
|
|
9820
|
-
track.stop();
|
|
9821
|
-
});
|
|
9822
|
-
// @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
|
|
9823
|
-
if (typeof stream.release === 'function') {
|
|
9824
|
-
// @ts-expect-error - release() is present in react-native-webrtc
|
|
9825
|
-
stream.release();
|
|
9826
|
-
}
|
|
9827
|
-
};
|
|
9828
9841
|
/**
|
|
9829
9842
|
* Resolves `default` device id into the real device id. Some browsers (notably,
|
|
9830
9843
|
* Chromium-based) report device with id `default` among audio input and output
|
|
@@ -10007,6 +10020,7 @@ class InputMediaDeviceManager {
|
|
|
10007
10020
|
entry.stop?.();
|
|
10008
10021
|
this.filters = this.filters.filter((f) => f !== entry);
|
|
10009
10022
|
await this.applySettingsToStream();
|
|
10023
|
+
this.call.tracer.trace(`unregisterFilter.${TrackType[this.trackType]}`, null);
|
|
10010
10024
|
}),
|
|
10011
10025
|
};
|
|
10012
10026
|
}
|
|
@@ -10026,7 +10040,7 @@ class InputMediaDeviceManager {
|
|
|
10026
10040
|
*/
|
|
10027
10041
|
async select(deviceId) {
|
|
10028
10042
|
if (isReactNative()) {
|
|
10029
|
-
throw new Error('This method is not supported in React Native.
|
|
10043
|
+
throw new Error('This method is not supported in React Native.');
|
|
10030
10044
|
}
|
|
10031
10045
|
const prevDeviceId = this.state.selectedDevice;
|
|
10032
10046
|
if (deviceId === prevDeviceId) {
|
|
@@ -10593,7 +10607,7 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
10593
10607
|
}
|
|
10594
10608
|
}
|
|
10595
10609
|
getDevices() {
|
|
10596
|
-
return getVideoDevices();
|
|
10610
|
+
return getVideoDevices(this.call.tracer);
|
|
10597
10611
|
}
|
|
10598
10612
|
getStream(constraints) {
|
|
10599
10613
|
constraints.width = this.targetResolution.width;
|
|
@@ -11000,6 +11014,7 @@ class MicrophoneManager extends InputMediaDeviceManager {
|
|
|
11000
11014
|
.catch((err) => {
|
|
11001
11015
|
this.logger('warn', 'Failed to unregister noise cancellation', err);
|
|
11002
11016
|
});
|
|
11017
|
+
this.call.tracer.trace('noiseCancellation.disabled', true);
|
|
11003
11018
|
await this.call.notifyNoiseCancellationStopped();
|
|
11004
11019
|
}
|
|
11005
11020
|
/**
|
|
@@ -11044,7 +11059,7 @@ class MicrophoneManager extends InputMediaDeviceManager {
|
|
|
11044
11059
|
}
|
|
11045
11060
|
}
|
|
11046
11061
|
getDevices() {
|
|
11047
|
-
return getAudioDevices();
|
|
11062
|
+
return getAudioDevices(this.call.tracer);
|
|
11048
11063
|
}
|
|
11049
11064
|
getStream(constraints) {
|
|
11050
11065
|
return getAudioStream(constraints, this.call.tracer);
|
|
@@ -11309,7 +11324,7 @@ class SpeakerManager {
|
|
|
11309
11324
|
if (isReactNative()) {
|
|
11310
11325
|
throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
|
|
11311
11326
|
}
|
|
11312
|
-
return getAudioOutputDevices();
|
|
11327
|
+
return getAudioOutputDevices(this.call.tracer);
|
|
11313
11328
|
}
|
|
11314
11329
|
/**
|
|
11315
11330
|
* Select a device.
|
|
@@ -11836,6 +11851,12 @@ class Call {
|
|
|
11836
11851
|
}
|
|
11837
11852
|
catch (err) {
|
|
11838
11853
|
this.logger('warn', `Failed to join call (${attempt})`, this.cid);
|
|
11854
|
+
if (err instanceof ErrorFromResponse && err.unrecoverable) {
|
|
11855
|
+
// if the error is unrecoverable, we should not retry as that signals
|
|
11856
|
+
// that connectivity is good, but the coordinator doesn't allow the user
|
|
11857
|
+
// to join the call due to some reason (e.g., ended call, expired token...)
|
|
11858
|
+
throw err;
|
|
11859
|
+
}
|
|
11839
11860
|
const sfuId = this.credentials?.server.edge_name || '';
|
|
11840
11861
|
const failures = (sfuJoinFailures.get(sfuId) || 0) + 1;
|
|
11841
11862
|
sfuJoinFailures.set(sfuId, failures);
|
|
@@ -12647,9 +12668,12 @@ class Call {
|
|
|
12647
12668
|
* @internal
|
|
12648
12669
|
*/
|
|
12649
12670
|
this.notifyTrackMuteState = async (muted, ...trackTypes) => {
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12671
|
+
const key = `muteState.${this.cid}.${trackTypes.join('-')}`;
|
|
12672
|
+
await withoutConcurrency(key, async () => {
|
|
12673
|
+
if (!this.sfuClient)
|
|
12674
|
+
return;
|
|
12675
|
+
await this.sfuClient.updateMuteStates(trackTypes.map((trackType) => ({ trackType, muted })));
|
|
12676
|
+
});
|
|
12653
12677
|
};
|
|
12654
12678
|
/**
|
|
12655
12679
|
* Will enhance the reported stats with additional participant-specific information (`callStatsReport$` state [store variable](./StreamVideoClient.md/#readonlystatestore)).
|
|
@@ -13561,7 +13585,6 @@ class StableWSConnection {
|
|
|
13561
13585
|
this.totalFailures += 1;
|
|
13562
13586
|
this._setHealth(false);
|
|
13563
13587
|
this.isConnecting = false;
|
|
13564
|
-
this.rejectConnectionOpen?.(new Error(`WebSocket error: ${event}`));
|
|
13565
13588
|
this._log(`onerror() - WS connection resulted into error`, { event });
|
|
13566
13589
|
this._reconnect();
|
|
13567
13590
|
};
|
|
@@ -14345,12 +14368,6 @@ class StreamClient {
|
|
|
14345
14368
|
response,
|
|
14346
14369
|
});
|
|
14347
14370
|
};
|
|
14348
|
-
this._logApiError = (type, url, error) => {
|
|
14349
|
-
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
14350
|
-
url,
|
|
14351
|
-
error,
|
|
14352
|
-
});
|
|
14353
|
-
};
|
|
14354
14371
|
this.doAxiosRequest = async (type, url, data, options = {}) => {
|
|
14355
14372
|
if (!options.publicEndpoint) {
|
|
14356
14373
|
await Promise.all([
|
|
@@ -14397,27 +14414,36 @@ class StreamClient {
|
|
|
14397
14414
|
}
|
|
14398
14415
|
this._logApiResponse(type, url, response);
|
|
14399
14416
|
this.consecutiveFailures = 0;
|
|
14400
|
-
return
|
|
14417
|
+
return response.data;
|
|
14401
14418
|
}
|
|
14402
14419
|
catch (e /**TODO: generalize error types */) {
|
|
14403
14420
|
e.client_request_id = requestConfig.headers?.['x-client-request-id'];
|
|
14404
14421
|
this.consecutiveFailures += 1;
|
|
14405
|
-
|
|
14406
|
-
|
|
14407
|
-
|
|
14408
|
-
|
|
14409
|
-
|
|
14410
|
-
|
|
14411
|
-
|
|
14412
|
-
|
|
14413
|
-
|
|
14414
|
-
|
|
14422
|
+
const { response } = e;
|
|
14423
|
+
if (!response || !isErrorResponse(response)) {
|
|
14424
|
+
this.logger('error', `client:${type} url: ${url}`, e);
|
|
14425
|
+
throw e;
|
|
14426
|
+
}
|
|
14427
|
+
const { data: responseData, status } = response;
|
|
14428
|
+
const isTokenExpired = responseData.code === KnownCodes.TOKEN_EXPIRED;
|
|
14429
|
+
if (isTokenExpired && !this.tokenManager.isStatic()) {
|
|
14430
|
+
this.logger('warn', `client:${type}: url: ${url}`, response);
|
|
14431
|
+
if (this.consecutiveFailures > 1) {
|
|
14432
|
+
await sleep(retryInterval(this.consecutiveFailures));
|
|
14415
14433
|
}
|
|
14416
|
-
|
|
14434
|
+
// refresh and retry the request
|
|
14435
|
+
await this.tokenManager.loadToken();
|
|
14436
|
+
return await this.doAxiosRequest(type, url, data, options);
|
|
14417
14437
|
}
|
|
14418
14438
|
else {
|
|
14419
|
-
this.
|
|
14420
|
-
throw
|
|
14439
|
+
this.logger('error', `client:${type} url: ${url}`, response);
|
|
14440
|
+
throw new ErrorFromResponse({
|
|
14441
|
+
message: `Stream error code ${responseData.code}: ${responseData.message}`,
|
|
14442
|
+
code: responseData.code ?? null,
|
|
14443
|
+
unrecoverable: responseData.unrecoverable ?? null,
|
|
14444
|
+
response: response,
|
|
14445
|
+
status: status,
|
|
14446
|
+
});
|
|
14421
14447
|
}
|
|
14422
14448
|
}
|
|
14423
14449
|
};
|
|
@@ -14440,23 +14466,6 @@ class StreamClient {
|
|
|
14440
14466
|
params,
|
|
14441
14467
|
});
|
|
14442
14468
|
};
|
|
14443
|
-
this.errorFromResponse = (response) => {
|
|
14444
|
-
const { data, status } = response;
|
|
14445
|
-
return new ErrorFromResponse({
|
|
14446
|
-
message: `Stream error code ${data.code}: ${data.message}`,
|
|
14447
|
-
code: data.code ?? null,
|
|
14448
|
-
unrecoverable: data.unrecoverable ?? null,
|
|
14449
|
-
response: response,
|
|
14450
|
-
status: status,
|
|
14451
|
-
});
|
|
14452
|
-
};
|
|
14453
|
-
this.handleResponse = (response) => {
|
|
14454
|
-
const data = response.data;
|
|
14455
|
-
if (isErrorResponse(response)) {
|
|
14456
|
-
throw this.errorFromResponse(response);
|
|
14457
|
-
}
|
|
14458
|
-
return data;
|
|
14459
|
-
};
|
|
14460
14469
|
this.dispatchEvent = (event) => {
|
|
14461
14470
|
this.logger('debug', `Dispatching event: ${event.type}`, event);
|
|
14462
14471
|
if (!this.listeners)
|
|
@@ -14489,7 +14498,7 @@ class StreamClient {
|
|
|
14489
14498
|
this.getUserAgent = () => {
|
|
14490
14499
|
if (!this.cachedUserAgent) {
|
|
14491
14500
|
const { clientAppIdentifier = {} } = this.options;
|
|
14492
|
-
const { sdkName = 'js', sdkVersion = "1.27.
|
|
14501
|
+
const { sdkName = 'js', sdkVersion = "1.27.2", ...extras } = clientAppIdentifier;
|
|
14493
14502
|
this.cachedUserAgent = [
|
|
14494
14503
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
14495
14504
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|