@stream-io/video-client 1.40.0 → 1.40.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 +13 -0
- package/dist/index.browser.es.js +72 -48
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +72 -47
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +72 -48
- package/dist/index.es.js.map +1 -1
- package/dist/src/StreamSfuClient.d.ts +6 -1
- package/dist/src/devices/DeviceManager.d.ts +13 -0
- package/package.json +1 -1
- package/src/Call.ts +5 -2
- package/src/StreamSfuClient.ts +29 -1
- package/src/devices/CameraManager.ts +26 -38
- package/src/devices/DeviceManager.ts +14 -0
- package/src/devices/MicrophoneManager.ts +10 -14
- package/src/devices/__tests__/CameraManager.test.ts +6 -6
- package/src/devices/__tests__/MicrophoneManager.test.ts +2 -9
package/dist/index.cjs.js
CHANGED
|
@@ -6080,7 +6080,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6080
6080
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6081
6081
|
};
|
|
6082
6082
|
|
|
6083
|
-
const version = "1.40.
|
|
6083
|
+
const version = "1.40.2";
|
|
6084
6084
|
const [major, minor, patch] = version.split('.');
|
|
6085
6085
|
let sdkInfo = {
|
|
6086
6086
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -8513,14 +8513,27 @@ class StreamSfuClient {
|
|
|
8513
8513
|
// be replaced with a new one in case a second join request is made
|
|
8514
8514
|
const current = this.joinResponseTask;
|
|
8515
8515
|
let timeoutId = undefined;
|
|
8516
|
+
const unsubscribeJoinErrorEvents = this.dispatcher.on('error', (event) => {
|
|
8517
|
+
const { error, reconnectStrategy } = event;
|
|
8518
|
+
if (!error)
|
|
8519
|
+
return;
|
|
8520
|
+
if (reconnectStrategy === WebsocketReconnectStrategy.DISCONNECT) {
|
|
8521
|
+
clearTimeout(timeoutId);
|
|
8522
|
+
unsubscribe?.();
|
|
8523
|
+
unsubscribeJoinErrorEvents();
|
|
8524
|
+
current.reject(new SfuJoinError(event));
|
|
8525
|
+
}
|
|
8526
|
+
});
|
|
8516
8527
|
const unsubscribe = this.dispatcher.on('joinResponse', (joinResponse) => {
|
|
8517
8528
|
clearTimeout(timeoutId);
|
|
8518
8529
|
unsubscribe();
|
|
8530
|
+
unsubscribeJoinErrorEvents();
|
|
8519
8531
|
this.keepAlive();
|
|
8520
8532
|
current.resolve(joinResponse);
|
|
8521
8533
|
});
|
|
8522
8534
|
timeoutId = setTimeout(() => {
|
|
8523
8535
|
unsubscribe();
|
|
8536
|
+
unsubscribeJoinErrorEvents();
|
|
8524
8537
|
const message = `Waiting for "joinResponse" has timed out after ${this.joinResponseTimeout}ms`;
|
|
8525
8538
|
this.tracer?.trace('joinRequestTimeout', message);
|
|
8526
8539
|
current.reject(new Error(message));
|
|
@@ -8665,6 +8678,14 @@ StreamSfuClient.DISPOSE_OLD_SOCKET = 4100;
|
|
|
8665
8678
|
* The close code used when the client fails to join the call (on the SFU).
|
|
8666
8679
|
*/
|
|
8667
8680
|
StreamSfuClient.JOIN_FAILED = 4101;
|
|
8681
|
+
class SfuJoinError extends Error {
|
|
8682
|
+
constructor(event) {
|
|
8683
|
+
super(event.error?.message || 'Join Error');
|
|
8684
|
+
this.errorEvent = event;
|
|
8685
|
+
this.unrecoverable =
|
|
8686
|
+
event.reconnectStrategy === WebsocketReconnectStrategy.DISCONNECT;
|
|
8687
|
+
}
|
|
8688
|
+
}
|
|
8668
8689
|
|
|
8669
8690
|
/**
|
|
8670
8691
|
* Event handler that watched the delivery of `call.accepted`.
|
|
@@ -10318,6 +10339,19 @@ class DeviceManager {
|
|
|
10318
10339
|
* if true, stops the media stream when call is left
|
|
10319
10340
|
*/
|
|
10320
10341
|
this.stopOnLeave = true;
|
|
10342
|
+
/**
|
|
10343
|
+
* When `true`, the `apply()` method will skip automatically enabling/disabling
|
|
10344
|
+
* the device based on server defaults (`mic_default_on`, `camera_default_on`).
|
|
10345
|
+
*
|
|
10346
|
+
* This is useful when application code wants to handle device preferences
|
|
10347
|
+
* (e.g., persisted user preferences) and prevent server defaults from
|
|
10348
|
+
* overriding them.
|
|
10349
|
+
*
|
|
10350
|
+
* @default false
|
|
10351
|
+
*
|
|
10352
|
+
* @internal
|
|
10353
|
+
*/
|
|
10354
|
+
this.deferServerDefaults = false;
|
|
10321
10355
|
this.subscriptions = [];
|
|
10322
10356
|
this.areSubscriptionsSetUp = false;
|
|
10323
10357
|
this.isTrackStoppedDueToTrackEnd = false;
|
|
@@ -10996,8 +11030,15 @@ class CameraManager extends DeviceManager {
|
|
|
10996
11030
|
* @internal
|
|
10997
11031
|
*/
|
|
10998
11032
|
async selectTargetResolution(resolution) {
|
|
10999
|
-
|
|
11000
|
-
|
|
11033
|
+
// normalize target resolution to landscape format.
|
|
11034
|
+
// on mobile devices, the device itself adjusts the resolution to portrait or landscape
|
|
11035
|
+
// depending on the orientation of the device. using portrait resolution
|
|
11036
|
+
// will result in falling back to the default resolution (640x480).
|
|
11037
|
+
let { width, height } = resolution;
|
|
11038
|
+
if (width < height)
|
|
11039
|
+
[width, height] = [height, width];
|
|
11040
|
+
this.targetResolution.height = height;
|
|
11041
|
+
this.targetResolution.width = width;
|
|
11001
11042
|
if (this.state.optimisticStatus === 'enabled') {
|
|
11002
11043
|
try {
|
|
11003
11044
|
await this.statusChangeSettled();
|
|
@@ -11011,9 +11052,8 @@ class CameraManager extends DeviceManager {
|
|
|
11011
11052
|
const [videoTrack] = this.state.mediaStream.getVideoTracks();
|
|
11012
11053
|
if (!videoTrack)
|
|
11013
11054
|
return;
|
|
11014
|
-
const { width, height } = videoTrack.getSettings();
|
|
11015
|
-
if (
|
|
11016
|
-
height !== this.targetResolution.height) {
|
|
11055
|
+
const { width: w, height: h } = videoTrack.getSettings();
|
|
11056
|
+
if (w !== width || h !== height) {
|
|
11017
11057
|
await this.applySettingsToStream();
|
|
11018
11058
|
this.logger.debug(`${width}x${height} target resolution applied to media stream`);
|
|
11019
11059
|
}
|
|
@@ -11026,38 +11066,25 @@ class CameraManager extends DeviceManager {
|
|
|
11026
11066
|
* @param publish whether to publish the stream after applying the settings.
|
|
11027
11067
|
*/
|
|
11028
11068
|
async apply(settings, publish) {
|
|
11029
|
-
const hasPublishedVideo = !!this.call.state.localParticipant?.videoStream;
|
|
11030
|
-
const hasPermission = this.call.permissionsContext.hasPermission(OwnCapability.SEND_AUDIO);
|
|
11031
|
-
if (hasPublishedVideo || !hasPermission)
|
|
11032
|
-
return;
|
|
11033
11069
|
// Wait for any in progress camera operation
|
|
11034
11070
|
await this.statusChangeSettled();
|
|
11035
|
-
|
|
11036
|
-
//
|
|
11037
|
-
//
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
-
|
|
11071
|
+
await this.selectTargetResolution(settings.target_resolution);
|
|
11072
|
+
// apply a direction and enable the camera only if in "pristine" state
|
|
11073
|
+
// and server defaults are not deferred to application code
|
|
11074
|
+
const canPublish = this.call.permissionsContext.canPublish(this.trackType);
|
|
11075
|
+
if (this.state.status === undefined && !this.deferServerDefaults) {
|
|
11076
|
+
if (!this.state.direction && !this.state.selectedDevice) {
|
|
11077
|
+
const direction = settings.camera_facing === 'front' ? 'front' : 'back';
|
|
11078
|
+
await this.selectDirection(direction);
|
|
11079
|
+
}
|
|
11080
|
+
if (canPublish && settings.camera_default_on && settings.enabled) {
|
|
11081
|
+
await this.enable();
|
|
11082
|
+
}
|
|
11047
11083
|
}
|
|
11048
|
-
if (!publish)
|
|
11049
|
-
return;
|
|
11050
11084
|
const { mediaStream } = this.state;
|
|
11051
|
-
if (this.enabled && mediaStream) {
|
|
11052
|
-
// The camera is already enabled (e.g. lobby screen). Publish the stream
|
|
11085
|
+
if (canPublish && publish && this.enabled && mediaStream) {
|
|
11053
11086
|
await this.publishStream(mediaStream);
|
|
11054
11087
|
}
|
|
11055
|
-
else if (this.state.status === undefined &&
|
|
11056
|
-
camera_default_on &&
|
|
11057
|
-
enabled) {
|
|
11058
|
-
// Start camera if backend config specifies, and there is no local setting
|
|
11059
|
-
await this.enable();
|
|
11060
|
-
}
|
|
11061
11088
|
}
|
|
11062
11089
|
getDevices() {
|
|
11063
11090
|
return getVideoDevices(this.call.tracer);
|
|
@@ -11565,24 +11592,20 @@ class MicrophoneManager extends AudioDeviceManager {
|
|
|
11565
11592
|
* @param publish whether to publish the stream after applying the settings.
|
|
11566
11593
|
*/
|
|
11567
11594
|
async apply(settings, publish) {
|
|
11568
|
-
if (!publish)
|
|
11569
|
-
return;
|
|
11570
|
-
const hasPublishedAudio = !!this.call.state.localParticipant?.audioStream;
|
|
11571
|
-
const hasPermission = this.call.permissionsContext.hasPermission(OwnCapability.SEND_AUDIO);
|
|
11572
|
-
if (hasPublishedAudio || !hasPermission)
|
|
11573
|
-
return;
|
|
11574
11595
|
// Wait for any in progress mic operation
|
|
11575
11596
|
await this.statusChangeSettled();
|
|
11576
|
-
|
|
11597
|
+
const canPublish = this.call.permissionsContext.canPublish(this.trackType);
|
|
11598
|
+
// apply server-side settings only when the device state is pristine
|
|
11599
|
+
// and server defaults are not deferred to application code
|
|
11600
|
+
if (this.state.status === undefined && !this.deferServerDefaults) {
|
|
11601
|
+
if (canPublish && settings.mic_default_on) {
|
|
11602
|
+
await this.enable();
|
|
11603
|
+
}
|
|
11604
|
+
}
|
|
11577
11605
|
const { mediaStream } = this.state;
|
|
11578
|
-
if (this.enabled && mediaStream) {
|
|
11579
|
-
// The mic is already enabled (e.g. lobby screen). Publish the stream
|
|
11606
|
+
if (canPublish && publish && this.enabled && mediaStream) {
|
|
11580
11607
|
await this.publishStream(mediaStream);
|
|
11581
11608
|
}
|
|
11582
|
-
else if (this.state.status === undefined && settings.mic_default_on) {
|
|
11583
|
-
// Start mic if backend config specifies, and there is no local setting
|
|
11584
|
-
await this.enable();
|
|
11585
|
-
}
|
|
11586
11609
|
}
|
|
11587
11610
|
getDevices() {
|
|
11588
11611
|
return getAudioDevices(this.call.tracer);
|
|
@@ -12439,7 +12462,8 @@ class Call {
|
|
|
12439
12462
|
}
|
|
12440
12463
|
catch (err) {
|
|
12441
12464
|
this.logger.warn(`Failed to join call (${attempt})`, this.cid);
|
|
12442
|
-
if (err instanceof ErrorFromResponse && err.unrecoverable)
|
|
12465
|
+
if ((err instanceof ErrorFromResponse && err.unrecoverable) ||
|
|
12466
|
+
(err instanceof SfuJoinError && err.unrecoverable)) {
|
|
12443
12467
|
// if the error is unrecoverable, we should not retry as that signals
|
|
12444
12468
|
// that connectivity is good, but the coordinator doesn't allow the user
|
|
12445
12469
|
// to join the call due to some reason (e.g., ended call, expired token...)
|
|
@@ -15108,7 +15132,7 @@ class StreamClient {
|
|
|
15108
15132
|
this.getUserAgent = () => {
|
|
15109
15133
|
if (!this.cachedUserAgent) {
|
|
15110
15134
|
const { clientAppIdentifier = {} } = this.options;
|
|
15111
|
-
const { sdkName = 'js', sdkVersion = "1.40.
|
|
15135
|
+
const { sdkName = 'js', sdkVersion = "1.40.2", ...extras } = clientAppIdentifier;
|
|
15112
15136
|
this.cachedUserAgent = [
|
|
15113
15137
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
15114
15138
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -15786,6 +15810,7 @@ exports.RxUtils = rxUtils;
|
|
|
15786
15810
|
exports.ScreenShareManager = ScreenShareManager;
|
|
15787
15811
|
exports.ScreenShareState = ScreenShareState;
|
|
15788
15812
|
exports.SfuEvents = events;
|
|
15813
|
+
exports.SfuJoinError = SfuJoinError;
|
|
15789
15814
|
exports.SfuModels = models;
|
|
15790
15815
|
exports.SpeakerManager = SpeakerManager;
|
|
15791
15816
|
exports.SpeakerState = SpeakerState;
|