@stream-io/video-client 1.7.1 → 1.7.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.
- package/CHANGELOG.md +14 -0
- package/dist/index.browser.es.js +83 -37
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +83 -37
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +83 -37
- package/dist/index.es.js.map +1 -1
- package/dist/src/coordinator/connection/client.d.ts +4 -0
- package/dist/src/coordinator/connection/utils.d.ts +4 -0
- package/dist/src/devices/CameraManager.d.ts +19 -20
- package/dist/src/rtc/videoLayers.d.ts +4 -4
- package/dist/src/types.d.ts +7 -1
- package/package.json +1 -1
- package/src/__tests__/Call.test.ts +40 -0
- package/src/coordinator/connection/client.ts +25 -13
- package/src/coordinator/connection/connection.ts +11 -0
- package/src/coordinator/connection/utils.ts +11 -0
- package/src/devices/CameraManager.ts +31 -29
- package/src/devices/__tests__/CameraManager.test.ts +1 -3
- package/src/rtc/Publisher.ts +5 -16
- package/src/rtc/videoLayers.ts +9 -6
- package/src/types.ts +8 -1
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.7.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.7.2...@stream-io/video-client-1.7.3) (2024-09-24)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* do not always error out api calls when web socket initially failed ([#1495](https://github.com/GetStream/stream-video-js/issues/1495)) ([7cdb62e](https://github.com/GetStream/stream-video-js/commit/7cdb62e75cad56098ee81eabbcc63382f93fd218))
|
|
11
|
+
|
|
12
|
+
## [1.7.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.7.1...@stream-io/video-client-1.7.2) (2024-09-20)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* overridable bitrate and bitrate downscale factor ([#1493](https://github.com/GetStream/stream-video-js/issues/1493)) ([cce5d8e](https://github.com/GetStream/stream-video-js/commit/cce5d8e641a9182a1779952e4e62aa16ec21ab92))
|
|
18
|
+
|
|
5
19
|
## [1.7.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.7.0...@stream-io/video-client-1.7.1) (2024-09-20)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.browser.es.js
CHANGED
|
@@ -2875,6 +2875,13 @@ function convertErrorToJson(err) {
|
|
|
2875
2875
|
}
|
|
2876
2876
|
return jsonObj;
|
|
2877
2877
|
}
|
|
2878
|
+
/**
|
|
2879
|
+
* Informs if a promise is yet to be resolved or rejected
|
|
2880
|
+
*/
|
|
2881
|
+
async function isPromisePending(promise) {
|
|
2882
|
+
const emptyObj = {};
|
|
2883
|
+
return Promise.race([promise, emptyObj]).then((value) => (value === emptyObj ? true : false), () => false);
|
|
2884
|
+
}
|
|
2878
2885
|
/**
|
|
2879
2886
|
* isOnline safely return the navigator.online value for browser env
|
|
2880
2887
|
* if navigator is not in global object, it always return true
|
|
@@ -3013,7 +3020,7 @@ const retryable = async (rpc, signal) => {
|
|
|
3013
3020
|
return result;
|
|
3014
3021
|
};
|
|
3015
3022
|
|
|
3016
|
-
const version = "1.7.
|
|
3023
|
+
const version = "1.7.3";
|
|
3017
3024
|
const [major, minor, patch] = version.split('.');
|
|
3018
3025
|
let sdkInfo = {
|
|
3019
3026
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -3290,14 +3297,16 @@ const defaultBitratePerRid = {
|
|
|
3290
3297
|
*
|
|
3291
3298
|
* @param videoTrack the video track to find optimal layers for.
|
|
3292
3299
|
* @param targetResolution the expected target resolution.
|
|
3293
|
-
* @param
|
|
3300
|
+
* @param publishOptions the publish options for the track.
|
|
3294
3301
|
*/
|
|
3295
|
-
const findOptimalVideoLayers = (videoTrack, targetResolution = defaultTargetResolution,
|
|
3302
|
+
const findOptimalVideoLayers = (videoTrack, targetResolution = defaultTargetResolution, publishOptions) => {
|
|
3296
3303
|
const optimalVideoLayers = [];
|
|
3297
3304
|
const settings = videoTrack.getSettings();
|
|
3298
3305
|
const { width: w = 0, height: h = 0 } = settings;
|
|
3306
|
+
const { preferredBitrate, bitrateDownscaleFactor = 2 } = publishOptions || {};
|
|
3299
3307
|
const maxBitrate = getComputedMaxBitrate(targetResolution, w, h, preferredBitrate);
|
|
3300
3308
|
let downscaleFactor = 1;
|
|
3309
|
+
let bitrateFactor = 1;
|
|
3301
3310
|
['f', 'h', 'q'].forEach((rid) => {
|
|
3302
3311
|
// Reversing the order [f, h, q] to [q, h, f] as Chrome uses encoding index
|
|
3303
3312
|
// when deciding which layer to disable when CPU or bandwidth is constrained.
|
|
@@ -3307,11 +3316,12 @@ const findOptimalVideoLayers = (videoTrack, targetResolution = defaultTargetReso
|
|
|
3307
3316
|
rid,
|
|
3308
3317
|
width: Math.round(w / downscaleFactor),
|
|
3309
3318
|
height: Math.round(h / downscaleFactor),
|
|
3310
|
-
maxBitrate: Math.round(maxBitrate /
|
|
3319
|
+
maxBitrate: Math.round(maxBitrate / bitrateFactor) || defaultBitratePerRid[rid],
|
|
3311
3320
|
scaleResolutionDownBy: downscaleFactor,
|
|
3312
3321
|
maxFramerate: 30,
|
|
3313
3322
|
});
|
|
3314
3323
|
downscaleFactor *= 2;
|
|
3324
|
+
bitrateFactor *= bitrateDownscaleFactor;
|
|
3315
3325
|
});
|
|
3316
3326
|
// for simplicity, we start with all layers enabled, then this function
|
|
3317
3327
|
// will clear/reassign the layers that are not needed
|
|
@@ -3371,7 +3381,8 @@ const withSimulcastConstraints = (settings, optimalVideoLayers) => {
|
|
|
3371
3381
|
rid: ridMapping[index], // reassign rid
|
|
3372
3382
|
}));
|
|
3373
3383
|
};
|
|
3374
|
-
const findOptimalScreenSharingLayers = (videoTrack,
|
|
3384
|
+
const findOptimalScreenSharingLayers = (videoTrack, publishOptions, defaultMaxBitrate = 3000000) => {
|
|
3385
|
+
const { screenShareSettings: preferences } = publishOptions || {};
|
|
3375
3386
|
const settings = videoTrack.getSettings();
|
|
3376
3387
|
return [
|
|
3377
3388
|
{
|
|
@@ -5152,11 +5163,10 @@ class Publisher {
|
|
|
5152
5163
|
const targetResolution = settings?.video
|
|
5153
5164
|
.target_resolution;
|
|
5154
5165
|
const screenShareBitrate = settings?.screensharing.target_resolution?.bitrate;
|
|
5155
|
-
const { preferredBitrate, preferredCodec, screenShareSettings } = opts;
|
|
5156
5166
|
const videoEncodings = trackType === TrackType.VIDEO
|
|
5157
|
-
? findOptimalVideoLayers(track, targetResolution,
|
|
5167
|
+
? findOptimalVideoLayers(track, targetResolution, opts)
|
|
5158
5168
|
: trackType === TrackType.SCREEN_SHARE
|
|
5159
|
-
? findOptimalScreenSharingLayers(track,
|
|
5169
|
+
? findOptimalScreenSharingLayers(track, opts, screenShareBitrate)
|
|
5160
5170
|
: undefined;
|
|
5161
5171
|
// listen for 'ended' event on the track as it might be ended abruptly
|
|
5162
5172
|
// by an external factor as permission revokes, device disconnected, etc.
|
|
@@ -5176,6 +5186,7 @@ class Publisher {
|
|
|
5176
5186
|
this.transceiverInitOrder.push(trackType);
|
|
5177
5187
|
this.transceiverRegistry[trackType] = transceiver;
|
|
5178
5188
|
this.publishOptionsPerTrackType.set(trackType, opts);
|
|
5189
|
+
const { preferredCodec } = opts;
|
|
5179
5190
|
const codec = isReactNative() && trackType === TrackType.VIDEO && !preferredCodec
|
|
5180
5191
|
? getRNOptimalCodec()
|
|
5181
5192
|
: preferredCodec;
|
|
@@ -5494,9 +5505,9 @@ class Publisher {
|
|
|
5494
5505
|
const publishOpts = this.publishOptionsPerTrackType.get(trackType);
|
|
5495
5506
|
optimalLayers =
|
|
5496
5507
|
trackType === TrackType.VIDEO
|
|
5497
|
-
? findOptimalVideoLayers(track, targetResolution, publishOpts
|
|
5508
|
+
? findOptimalVideoLayers(track, targetResolution, publishOpts)
|
|
5498
5509
|
: trackType === TrackType.SCREEN_SHARE
|
|
5499
|
-
? findOptimalScreenSharingLayers(track, publishOpts
|
|
5510
|
+
? findOptimalScreenSharingLayers(track, publishOpts)
|
|
5500
5511
|
: [];
|
|
5501
5512
|
this.trackLayersCache[trackType] = optimalLayers;
|
|
5502
5513
|
}
|
|
@@ -8380,6 +8391,11 @@ class CameraManagerState extends InputMediaDeviceManagerState {
|
|
|
8380
8391
|
}
|
|
8381
8392
|
|
|
8382
8393
|
class CameraManager extends InputMediaDeviceManager {
|
|
8394
|
+
/**
|
|
8395
|
+
* Constructs a new CameraManager.
|
|
8396
|
+
*
|
|
8397
|
+
* @param call the call instance.
|
|
8398
|
+
*/
|
|
8383
8399
|
constructor(call) {
|
|
8384
8400
|
super(call, new CameraManagerState(), TrackType.VIDEO);
|
|
8385
8401
|
this.targetResolution = {
|
|
@@ -8387,17 +8403,6 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
8387
8403
|
height: 720,
|
|
8388
8404
|
};
|
|
8389
8405
|
}
|
|
8390
|
-
/**
|
|
8391
|
-
* The publish options for the camera.
|
|
8392
|
-
*
|
|
8393
|
-
* @internal internal use only, not part of the public API.
|
|
8394
|
-
*/
|
|
8395
|
-
get publishOptions() {
|
|
8396
|
-
return {
|
|
8397
|
-
preferredCodec: this.preferredCodec,
|
|
8398
|
-
preferredBitrate: this.preferredBitrate,
|
|
8399
|
-
};
|
|
8400
|
-
}
|
|
8401
8406
|
/**
|
|
8402
8407
|
* Select the camera direction.
|
|
8403
8408
|
*
|
|
@@ -8452,16 +8457,33 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
8452
8457
|
* @param codec the codec to use for encoding the video.
|
|
8453
8458
|
*/
|
|
8454
8459
|
setPreferredCodec(codec) {
|
|
8455
|
-
this.preferredCodec
|
|
8460
|
+
this.updatePublishOptions({ preferredCodec: codec });
|
|
8456
8461
|
}
|
|
8457
8462
|
/**
|
|
8458
|
-
*
|
|
8463
|
+
* Updates the preferred publish options for the video stream.
|
|
8459
8464
|
*
|
|
8460
|
-
* @internal
|
|
8461
|
-
* @param
|
|
8465
|
+
* @internal
|
|
8466
|
+
* @param options the options to use.
|
|
8462
8467
|
*/
|
|
8463
|
-
|
|
8464
|
-
this.
|
|
8468
|
+
updatePublishOptions(options) {
|
|
8469
|
+
this.publishOptions = { ...this.publishOptions, ...options };
|
|
8470
|
+
}
|
|
8471
|
+
/**
|
|
8472
|
+
* Returns the capture resolution of the camera.
|
|
8473
|
+
*/
|
|
8474
|
+
getCaptureResolution() {
|
|
8475
|
+
const { mediaStream } = this.state;
|
|
8476
|
+
if (!mediaStream)
|
|
8477
|
+
return;
|
|
8478
|
+
const [videoTrack] = mediaStream.getVideoTracks();
|
|
8479
|
+
if (!videoTrack)
|
|
8480
|
+
return;
|
|
8481
|
+
const settings = videoTrack.getSettings();
|
|
8482
|
+
return {
|
|
8483
|
+
width: settings.width,
|
|
8484
|
+
height: settings.height,
|
|
8485
|
+
frameRate: settings.frameRate,
|
|
8486
|
+
};
|
|
8465
8487
|
}
|
|
8466
8488
|
getDevices() {
|
|
8467
8489
|
return getVideoDevices();
|
|
@@ -11350,6 +11372,15 @@ class StableWSConnection {
|
|
|
11350
11372
|
this._log(`_connect() - tokenProvider failed before, so going to retry`);
|
|
11351
11373
|
await this.client.tokenManager.loadToken();
|
|
11352
11374
|
}
|
|
11375
|
+
let mustSetupConnectionIdPromise = true;
|
|
11376
|
+
if (this.client.connectionIdPromise) {
|
|
11377
|
+
if (await isPromisePending(this.client.connectionIdPromise)) {
|
|
11378
|
+
mustSetupConnectionIdPromise = false;
|
|
11379
|
+
}
|
|
11380
|
+
}
|
|
11381
|
+
if (mustSetupConnectionIdPromise) {
|
|
11382
|
+
this.client._setupConnectionIdPromise();
|
|
11383
|
+
}
|
|
11353
11384
|
this._setupConnectionPromise();
|
|
11354
11385
|
const wsURL = this._buildUrl();
|
|
11355
11386
|
this._log(`_connect() - Connecting to ${wsURL}`, {
|
|
@@ -11375,6 +11406,7 @@ class StableWSConnection {
|
|
|
11375
11406
|
}
|
|
11376
11407
|
}
|
|
11377
11408
|
catch (err) {
|
|
11409
|
+
await this.client._setupConnectionIdPromise();
|
|
11378
11410
|
this.isConnecting = false;
|
|
11379
11411
|
// @ts-ignore
|
|
11380
11412
|
this._log(`_connect() - Error - `, err);
|
|
@@ -12032,10 +12064,7 @@ class StreamClient {
|
|
|
12032
12064
|
this.logger('info', 'client:openConnection() - openConnection called twice, healthy connection already exists');
|
|
12033
12065
|
return Promise.resolve();
|
|
12034
12066
|
}
|
|
12035
|
-
this.
|
|
12036
|
-
this.resolveConnectionId = resolve;
|
|
12037
|
-
this.rejectConnectionId = reject;
|
|
12038
|
-
});
|
|
12067
|
+
this._setupConnectionIdPromise();
|
|
12039
12068
|
this.clientID = `${this.userID}--${randomId()}`;
|
|
12040
12069
|
this.wsPromise = this.connect();
|
|
12041
12070
|
return this.wsPromise;
|
|
@@ -12075,10 +12104,7 @@ class StreamClient {
|
|
|
12075
12104
|
*/
|
|
12076
12105
|
this.connectAnonymousUser = async (user, tokenOrProvider) => {
|
|
12077
12106
|
addConnectionEventListeners(this.updateNetworkConnectionStatus);
|
|
12078
|
-
this.
|
|
12079
|
-
this.resolveConnectionId = resolve;
|
|
12080
|
-
this.rejectConnectionId = reject;
|
|
12081
|
-
});
|
|
12107
|
+
this._setupConnectionIdPromise();
|
|
12082
12108
|
this.anonymous = true;
|
|
12083
12109
|
await this._setToken(user, tokenOrProvider, this.anonymous);
|
|
12084
12110
|
this._setUser(user);
|
|
@@ -12117,6 +12143,16 @@ class StreamClient {
|
|
|
12117
12143
|
this.logger('debug', `Removing listener for ${eventName} event`);
|
|
12118
12144
|
this.listeners[eventName] = this.listeners[eventName]?.filter((value) => value !== callback);
|
|
12119
12145
|
};
|
|
12146
|
+
/**
|
|
12147
|
+
* sets up the this.connectionIdPromise
|
|
12148
|
+
*/
|
|
12149
|
+
this._setupConnectionIdPromise = async () => {
|
|
12150
|
+
/** a promise that is resolved once connection id is set */
|
|
12151
|
+
this.connectionIdPromise = new Promise((resolve, reject) => {
|
|
12152
|
+
this.resolveConnectionId = resolve;
|
|
12153
|
+
this.rejectConnectionId = reject;
|
|
12154
|
+
});
|
|
12155
|
+
};
|
|
12120
12156
|
this._logApiRequest = (type, url, data, config) => {
|
|
12121
12157
|
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
12122
12158
|
payload: data,
|
|
@@ -12139,8 +12175,18 @@ class StreamClient {
|
|
|
12139
12175
|
await Promise.all([
|
|
12140
12176
|
this.tokenManager.tokenReady(),
|
|
12141
12177
|
this.guestUserCreatePromise,
|
|
12142
|
-
this.connectionIdPromise,
|
|
12143
12178
|
]);
|
|
12179
|
+
// we need to wait for presence of connection id before making requests
|
|
12180
|
+
try {
|
|
12181
|
+
await this.connectionIdPromise;
|
|
12182
|
+
}
|
|
12183
|
+
catch (e) {
|
|
12184
|
+
// in case connection id was rejected
|
|
12185
|
+
// reconnection maybe in progress
|
|
12186
|
+
// we can wait for healthy connection to resolve, which rejects when 15s timeout is reached
|
|
12187
|
+
await this.wsConnection?._waitForHealthy();
|
|
12188
|
+
await this.connectionIdPromise;
|
|
12189
|
+
}
|
|
12144
12190
|
}
|
|
12145
12191
|
const requestConfig = this._enrichAxiosOptions(options);
|
|
12146
12192
|
try {
|
|
@@ -12323,7 +12369,7 @@ class StreamClient {
|
|
|
12323
12369
|
});
|
|
12324
12370
|
};
|
|
12325
12371
|
this.getUserAgent = () => {
|
|
12326
|
-
const version = "1.7.
|
|
12372
|
+
const version = "1.7.3";
|
|
12327
12373
|
return (this.userAgent ||
|
|
12328
12374
|
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
|
|
12329
12375
|
};
|