@stream-io/video-client 1.26.1 → 1.27.0
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 +10 -0
- package/dist/index.browser.es.js +235 -47
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +235 -46
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +235 -47
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +14 -2
- package/dist/src/StreamSfuClient.d.ts +7 -3
- package/dist/src/events/internal.d.ts +7 -1
- package/dist/src/gen/video/sfu/event/events.d.ts +57 -1
- package/dist/src/gen/video/sfu/models/models.d.ts +21 -0
- package/dist/src/helpers/array.d.ts +7 -0
- package/dist/src/helpers/participantUtils.d.ts +8 -1
- package/dist/src/rtc/BasePeerConnection.d.ts +2 -2
- package/dist/src/rtc/Dispatcher.d.ts +1 -1
- package/dist/src/rtc/signal.d.ts +1 -1
- package/dist/src/store/CallState.d.ts +2 -1
- package/dist/src/types.d.ts +10 -1
- package/package.json +1 -1
- package/src/Call.ts +48 -5
- package/src/StreamSfuClient.ts +33 -14
- package/src/coordinator/connection/connection.ts +0 -1
- package/src/events/__tests__/internal.test.ts +78 -0
- package/src/events/__tests__/participant.test.ts +66 -0
- package/src/events/callEventHandlers.ts +2 -0
- package/src/events/internal.ts +28 -1
- package/src/events/participant.ts +4 -1
- package/src/gen/video/sfu/event/events.ts +104 -0
- package/src/gen/video/sfu/models/models.ts +21 -0
- package/src/helpers/__tests__/participantUtils.test.ts +167 -0
- package/src/helpers/array.ts +16 -0
- package/src/helpers/participantUtils.ts +23 -1
- package/src/rtc/BasePeerConnection.ts +6 -5
- package/src/rtc/Dispatcher.ts +3 -2
- package/src/rtc/__tests__/Publisher.test.ts +3 -2
- package/src/rtc/__tests__/Subscriber.test.ts +3 -2
- package/src/rtc/signal.ts +3 -3
- package/src/store/CallState.ts +7 -4
- package/src/types.ts +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [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
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- Inbound Video Pause ([#1841](https://github.com/GetStream/stream-video-js/issues/1841)) ([5c7eb3a](https://github.com/GetStream/stream-video-js/commit/5c7eb3ac8b0fcfd663226d537279c8a941dedc21))
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
- more graceful handling of SFU join failures ([#1853](https://github.com/GetStream/stream-video-js/issues/1853)) ([f38a4b5](https://github.com/GetStream/stream-video-js/commit/f38a4b5eef62210b08424640040a88065b680707))
|
|
14
|
+
|
|
5
15
|
## [1.26.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.26.0...@stream-io/video-client-1.26.1) (2025-07-17)
|
|
6
16
|
|
|
7
17
|
### Bug Fixes
|
package/dist/index.browser.es.js
CHANGED
|
@@ -971,6 +971,10 @@ var ErrorCode;
|
|
|
971
971
|
* @generated from protobuf enum value: ERROR_CODE_CALL_NOT_FOUND = 300;
|
|
972
972
|
*/
|
|
973
973
|
ErrorCode[ErrorCode["CALL_NOT_FOUND"] = 300] = "CALL_NOT_FOUND";
|
|
974
|
+
/**
|
|
975
|
+
* @generated from protobuf enum value: ERROR_CODE_CALL_PARTICIPANT_LIMIT_REACHED = 301;
|
|
976
|
+
*/
|
|
977
|
+
ErrorCode[ErrorCode["CALL_PARTICIPANT_LIMIT_REACHED"] = 301] = "CALL_PARTICIPANT_LIMIT_REACHED";
|
|
974
978
|
/**
|
|
975
979
|
* @generated from protobuf enum value: ERROR_CODE_REQUEST_VALIDATION_FAILED = 400;
|
|
976
980
|
*/
|
|
@@ -1239,6 +1243,24 @@ var AppleThermalState;
|
|
|
1239
1243
|
*/
|
|
1240
1244
|
AppleThermalState[AppleThermalState["CRITICAL"] = 4] = "CRITICAL";
|
|
1241
1245
|
})(AppleThermalState || (AppleThermalState = {}));
|
|
1246
|
+
/**
|
|
1247
|
+
* ClientCapability defines a feature that client supports
|
|
1248
|
+
*
|
|
1249
|
+
* @generated from protobuf enum stream.video.sfu.models.ClientCapability
|
|
1250
|
+
*/
|
|
1251
|
+
var ClientCapability;
|
|
1252
|
+
(function (ClientCapability) {
|
|
1253
|
+
/**
|
|
1254
|
+
* @generated from protobuf enum value: CLIENT_CAPABILITY_UNSPECIFIED = 0;
|
|
1255
|
+
*/
|
|
1256
|
+
ClientCapability[ClientCapability["UNSPECIFIED"] = 0] = "UNSPECIFIED";
|
|
1257
|
+
/**
|
|
1258
|
+
* Enables SFU pausing inbound video
|
|
1259
|
+
*
|
|
1260
|
+
* @generated from protobuf enum value: CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE = 1;
|
|
1261
|
+
*/
|
|
1262
|
+
ClientCapability[ClientCapability["SUBSCRIBER_VIDEO_PAUSE"] = 1] = "SUBSCRIBER_VIDEO_PAUSE";
|
|
1263
|
+
})(ClientCapability || (ClientCapability = {}));
|
|
1242
1264
|
// @generated message type with reflection information, may provide speed optimized methods
|
|
1243
1265
|
class CallState$Type extends MessageType {
|
|
1244
1266
|
constructor() {
|
|
@@ -1909,6 +1931,7 @@ var models = /*#__PURE__*/Object.freeze({
|
|
|
1909
1931
|
get CallEndedReason () { return CallEndedReason; },
|
|
1910
1932
|
CallGrants: CallGrants,
|
|
1911
1933
|
CallState: CallState$1,
|
|
1934
|
+
get ClientCapability () { return ClientCapability; },
|
|
1912
1935
|
ClientDetails: ClientDetails,
|
|
1913
1936
|
Codec: Codec,
|
|
1914
1937
|
get ConnectionQuality () { return ConnectionQuality; },
|
|
@@ -2598,6 +2621,13 @@ class SfuEvent$Type extends MessageType {
|
|
|
2598
2621
|
oneof: 'eventPayload',
|
|
2599
2622
|
T: () => ChangePublishOptions,
|
|
2600
2623
|
},
|
|
2624
|
+
{
|
|
2625
|
+
no: 28,
|
|
2626
|
+
name: 'inbound_state_notification',
|
|
2627
|
+
kind: 'message',
|
|
2628
|
+
oneof: 'eventPayload',
|
|
2629
|
+
T: () => InboundStateNotification,
|
|
2630
|
+
},
|
|
2601
2631
|
]);
|
|
2602
2632
|
}
|
|
2603
2633
|
}
|
|
@@ -2905,6 +2935,17 @@ class JoinRequest$Type extends MessageType {
|
|
|
2905
2935
|
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
2906
2936
|
T: () => SubscribeOption,
|
|
2907
2937
|
},
|
|
2938
|
+
{
|
|
2939
|
+
no: 11,
|
|
2940
|
+
name: 'capabilities',
|
|
2941
|
+
kind: 'enum',
|
|
2942
|
+
repeat: 1 /*RepeatType.PACKED*/,
|
|
2943
|
+
T: () => [
|
|
2944
|
+
'stream.video.sfu.models.ClientCapability',
|
|
2945
|
+
ClientCapability,
|
|
2946
|
+
'CLIENT_CAPABILITY_',
|
|
2947
|
+
],
|
|
2948
|
+
},
|
|
2908
2949
|
]);
|
|
2909
2950
|
}
|
|
2910
2951
|
}
|
|
@@ -3350,6 +3391,48 @@ class CallEnded$Type extends MessageType {
|
|
|
3350
3391
|
* @generated MessageType for protobuf message stream.video.sfu.event.CallEnded
|
|
3351
3392
|
*/
|
|
3352
3393
|
const CallEnded = new CallEnded$Type();
|
|
3394
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
3395
|
+
class InboundStateNotification$Type extends MessageType {
|
|
3396
|
+
constructor() {
|
|
3397
|
+
super('stream.video.sfu.event.InboundStateNotification', [
|
|
3398
|
+
{
|
|
3399
|
+
no: 1,
|
|
3400
|
+
name: 'inbound_video_states',
|
|
3401
|
+
kind: 'message',
|
|
3402
|
+
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
3403
|
+
T: () => InboundVideoState,
|
|
3404
|
+
},
|
|
3405
|
+
]);
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
/**
|
|
3409
|
+
* @generated MessageType for protobuf message stream.video.sfu.event.InboundStateNotification
|
|
3410
|
+
*/
|
|
3411
|
+
const InboundStateNotification = new InboundStateNotification$Type();
|
|
3412
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
3413
|
+
class InboundVideoState$Type extends MessageType {
|
|
3414
|
+
constructor() {
|
|
3415
|
+
super('stream.video.sfu.event.InboundVideoState', [
|
|
3416
|
+
{ no: 1, name: 'user_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
3417
|
+
{ no: 2, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
3418
|
+
{
|
|
3419
|
+
no: 3,
|
|
3420
|
+
name: 'track_type',
|
|
3421
|
+
kind: 'enum',
|
|
3422
|
+
T: () => [
|
|
3423
|
+
'stream.video.sfu.models.TrackType',
|
|
3424
|
+
TrackType,
|
|
3425
|
+
'TRACK_TYPE_',
|
|
3426
|
+
],
|
|
3427
|
+
},
|
|
3428
|
+
{ no: 4, name: 'paused', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
|
|
3429
|
+
]);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
/**
|
|
3433
|
+
* @generated MessageType for protobuf message stream.video.sfu.event.InboundVideoState
|
|
3434
|
+
*/
|
|
3435
|
+
const InboundVideoState = new InboundVideoState$Type();
|
|
3353
3436
|
|
|
3354
3437
|
var events = /*#__PURE__*/Object.freeze({
|
|
3355
3438
|
__proto__: null,
|
|
@@ -3370,6 +3453,8 @@ var events = /*#__PURE__*/Object.freeze({
|
|
|
3370
3453
|
HealthCheckResponse: HealthCheckResponse,
|
|
3371
3454
|
ICERestart: ICERestart,
|
|
3372
3455
|
ICETrickle: ICETrickle,
|
|
3456
|
+
InboundStateNotification: InboundStateNotification,
|
|
3457
|
+
InboundVideoState: InboundVideoState,
|
|
3373
3458
|
JoinRequest: JoinRequest,
|
|
3374
3459
|
JoinResponse: JoinResponse,
|
|
3375
3460
|
LeaveCallRequest: LeaveCallRequest,
|
|
@@ -3794,6 +3879,7 @@ const sfuEventKinds = {
|
|
|
3794
3879
|
participantUpdated: undefined,
|
|
3795
3880
|
participantMigrationComplete: undefined,
|
|
3796
3881
|
changePublishOptions: undefined,
|
|
3882
|
+
inboundStateNotification: undefined,
|
|
3797
3883
|
};
|
|
3798
3884
|
const isSfuEvent = (eventName) => {
|
|
3799
3885
|
return Object.prototype.hasOwnProperty.call(sfuEventKinds, eventName);
|
|
@@ -3802,12 +3888,12 @@ class Dispatcher {
|
|
|
3802
3888
|
constructor() {
|
|
3803
3889
|
this.logger = getLogger(['Dispatcher']);
|
|
3804
3890
|
this.subscribers = {};
|
|
3805
|
-
this.dispatch = (message,
|
|
3891
|
+
this.dispatch = (message, tag = '0') => {
|
|
3806
3892
|
const eventKind = message.eventPayload.oneofKind;
|
|
3807
3893
|
if (!eventKind)
|
|
3808
3894
|
return;
|
|
3809
3895
|
const payload = message.eventPayload[eventKind];
|
|
3810
|
-
this.logger('debug', `Dispatching ${eventKind}, tag=${
|
|
3896
|
+
this.logger('debug', `Dispatching ${eventKind}, tag=${tag}`, payload);
|
|
3811
3897
|
const listeners = this.subscribers[eventKind];
|
|
3812
3898
|
if (!listeners)
|
|
3813
3899
|
return;
|
|
@@ -4324,6 +4410,24 @@ const hasScreenShareAudio = (p) => p.publishedTracks.includes(TrackType.SCREEN_S
|
|
|
4324
4410
|
* @param p the participant.
|
|
4325
4411
|
*/
|
|
4326
4412
|
const isPinned = (p) => !!p.pin && (p.pin.isLocalPin || p.pin.pinnedAt > 0);
|
|
4413
|
+
/**
|
|
4414
|
+
* Check if a participant has a paused track of the specified type.
|
|
4415
|
+
*
|
|
4416
|
+
* @param p the participant to check.
|
|
4417
|
+
* @param videoTrackType the type of video track to check for ('videoTrack' or 'screenShareTrack').
|
|
4418
|
+
*/
|
|
4419
|
+
const hasPausedTrack = (p, videoTrackType) => {
|
|
4420
|
+
if (!p.pausedTracks)
|
|
4421
|
+
return false;
|
|
4422
|
+
const trackType = videoTrackType === 'videoTrack'
|
|
4423
|
+
? TrackType.VIDEO
|
|
4424
|
+
: videoTrackType === 'screenShareTrack'
|
|
4425
|
+
? TrackType.SCREEN_SHARE
|
|
4426
|
+
: undefined;
|
|
4427
|
+
if (!trackType)
|
|
4428
|
+
return false;
|
|
4429
|
+
return p.pausedTracks.includes(trackType);
|
|
4430
|
+
};
|
|
4327
4431
|
|
|
4328
4432
|
/**
|
|
4329
4433
|
* A comparator which sorts participants by the fact that they are the dominant speaker or not.
|
|
@@ -4726,17 +4830,17 @@ class CallState {
|
|
|
4726
4830
|
*
|
|
4727
4831
|
* @param sessionId the session ID of the participant to update.
|
|
4728
4832
|
* @param participant the participant to update or add.
|
|
4833
|
+
* @param patch an optional patch to apply to the participant.
|
|
4729
4834
|
*/
|
|
4730
|
-
this.updateOrAddParticipant = (sessionId, participant) => {
|
|
4835
|
+
this.updateOrAddParticipant = (sessionId, participant, patch) => {
|
|
4731
4836
|
return this.setParticipants((participants) => {
|
|
4732
4837
|
let add = true;
|
|
4733
4838
|
const nextParticipants = participants.map((p) => {
|
|
4734
4839
|
if (p.sessionId === sessionId) {
|
|
4735
4840
|
add = false;
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
};
|
|
4841
|
+
const updated = { ...p, ...participant };
|
|
4842
|
+
const thePatch = typeof patch === 'function' ? patch(updated) : patch;
|
|
4843
|
+
return Object.assign(updated, thePatch);
|
|
4740
4844
|
}
|
|
4741
4845
|
return p;
|
|
4742
4846
|
});
|
|
@@ -5810,7 +5914,7 @@ const aggregate = (stats) => {
|
|
|
5810
5914
|
return report;
|
|
5811
5915
|
};
|
|
5812
5916
|
|
|
5813
|
-
const version = "1.
|
|
5917
|
+
const version = "1.27.0";
|
|
5814
5918
|
const [major, minor, patch] = version.split('.');
|
|
5815
5919
|
let sdkInfo = {
|
|
5816
5920
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -6419,7 +6523,7 @@ class BasePeerConnection {
|
|
|
6419
6523
|
/**
|
|
6420
6524
|
* Constructs a new `BasePeerConnection` instance.
|
|
6421
6525
|
*/
|
|
6422
|
-
constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded,
|
|
6526
|
+
constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, tag, enableTracing, iceRestartDelay = 2500, }) {
|
|
6423
6527
|
this.isIceRestarting = false;
|
|
6424
6528
|
this.isDisposed = false;
|
|
6425
6529
|
this.trackIdToTrackType = new Map();
|
|
@@ -6643,13 +6747,12 @@ class BasePeerConnection {
|
|
|
6643
6747
|
this.onReconnectionNeeded = onReconnectionNeeded;
|
|
6644
6748
|
this.logger = getLogger([
|
|
6645
6749
|
peerType === PeerType.SUBSCRIBER ? 'Subscriber' : 'Publisher',
|
|
6646
|
-
|
|
6750
|
+
tag,
|
|
6647
6751
|
]);
|
|
6648
6752
|
this.pc = this.createPeerConnection(connectionConfig);
|
|
6649
6753
|
this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType);
|
|
6650
6754
|
if (enableTracing) {
|
|
6651
|
-
|
|
6652
|
-
this.tracer = new Tracer(tag);
|
|
6755
|
+
this.tracer = new Tracer(`${tag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}`);
|
|
6653
6756
|
this.tracer.trace('create', {
|
|
6654
6757
|
url: sfuClient.edgeName,
|
|
6655
6758
|
...connectionConfig,
|
|
@@ -7521,8 +7624,8 @@ class Subscriber extends BasePeerConnection {
|
|
|
7521
7624
|
}
|
|
7522
7625
|
|
|
7523
7626
|
const createWebSocketSignalChannel = (opts) => {
|
|
7524
|
-
const { endpoint, onMessage,
|
|
7525
|
-
const logger = getLogger(['SfuClientWS',
|
|
7627
|
+
const { endpoint, onMessage, tag } = opts;
|
|
7628
|
+
const logger = getLogger(['SfuClientWS', tag]);
|
|
7526
7629
|
logger('debug', 'Creating signaling WS channel:', endpoint);
|
|
7527
7630
|
const ws = new WebSocket(endpoint);
|
|
7528
7631
|
ws.binaryType = 'arraybuffer'; // do we need this?
|
|
@@ -7772,7 +7875,7 @@ class StreamSfuClient {
|
|
|
7772
7875
|
/**
|
|
7773
7876
|
* Constructs a new SFU client.
|
|
7774
7877
|
*/
|
|
7775
|
-
constructor({ dispatcher, credentials, sessionId,
|
|
7878
|
+
constructor({ dispatcher, credentials, sessionId, cid, tag, joinResponseTimeout = 5000, onSignalClose, streamClient, enableTracing, }) {
|
|
7776
7879
|
/**
|
|
7777
7880
|
* A buffer for ICE Candidates that are received before
|
|
7778
7881
|
* the Publisher and Subscriber Peer Connections are ready to handle them.
|
|
@@ -7802,7 +7905,7 @@ class StreamSfuClient {
|
|
|
7802
7905
|
* A controller to abort the current requests.
|
|
7803
7906
|
*/
|
|
7804
7907
|
this.abortController = new AbortController();
|
|
7805
|
-
this.createWebSocket = () => {
|
|
7908
|
+
this.createWebSocket = (params) => {
|
|
7806
7909
|
const eventsToTrace = {
|
|
7807
7910
|
callEnded: true,
|
|
7808
7911
|
changePublishQuality: true,
|
|
@@ -7810,10 +7913,11 @@ class StreamSfuClient {
|
|
|
7810
7913
|
connectionQualityChanged: true,
|
|
7811
7914
|
error: true,
|
|
7812
7915
|
goAway: true,
|
|
7916
|
+
inboundStateNotification: true,
|
|
7813
7917
|
};
|
|
7814
7918
|
this.signalWs = createWebSocketSignalChannel({
|
|
7815
|
-
|
|
7816
|
-
endpoint: `${this.credentials.server.ws_endpoint}
|
|
7919
|
+
tag: this.tag,
|
|
7920
|
+
endpoint: `${this.credentials.server.ws_endpoint}?${new URLSearchParams(params).toString()}`,
|
|
7817
7921
|
onMessage: (message) => {
|
|
7818
7922
|
this.lastMessageTimestamp = new Date();
|
|
7819
7923
|
this.scheduleConnectionCheck();
|
|
@@ -7821,7 +7925,7 @@ class StreamSfuClient {
|
|
|
7821
7925
|
if (eventsToTrace[eventKind]) {
|
|
7822
7926
|
this.tracer?.trace(eventKind, message);
|
|
7823
7927
|
}
|
|
7824
|
-
this.dispatcher.dispatch(message, this.
|
|
7928
|
+
this.dispatcher.dispatch(message, this.tag);
|
|
7825
7929
|
},
|
|
7826
7930
|
});
|
|
7827
7931
|
this.signalReady = makeSafePromise(Promise.race([
|
|
@@ -7936,7 +8040,7 @@ class StreamSfuClient {
|
|
|
7936
8040
|
});
|
|
7937
8041
|
this.migrateAwayTimeout = setTimeout(() => {
|
|
7938
8042
|
unsubscribe();
|
|
7939
|
-
task.reject(new Error(`Migration (${this.
|
|
8043
|
+
task.reject(new Error(`Migration (${this.tag}) failed to complete in ${timeout}ms`));
|
|
7940
8044
|
}, timeout);
|
|
7941
8045
|
return task.promise;
|
|
7942
8046
|
};
|
|
@@ -8036,10 +8140,10 @@ class StreamSfuClient {
|
|
|
8036
8140
|
const { server, token } = credentials;
|
|
8037
8141
|
this.edgeName = server.edge_name;
|
|
8038
8142
|
this.joinResponseTimeout = joinResponseTimeout;
|
|
8039
|
-
this.
|
|
8040
|
-
this.logger = getLogger(['SfuClient',
|
|
8143
|
+
this.tag = tag;
|
|
8144
|
+
this.logger = getLogger(['SfuClient', tag]);
|
|
8041
8145
|
this.tracer = enableTracing
|
|
8042
|
-
? new Tracer(`${
|
|
8146
|
+
? new Tracer(`${tag}-${this.edgeName}`)
|
|
8043
8147
|
: undefined;
|
|
8044
8148
|
this.rpc = createSignalClient({
|
|
8045
8149
|
baseUrl: server.url,
|
|
@@ -8068,7 +8172,13 @@ class StreamSfuClient {
|
|
|
8068
8172
|
this.networkAvailableTask?.resolve();
|
|
8069
8173
|
}
|
|
8070
8174
|
});
|
|
8071
|
-
this.createWebSocket(
|
|
8175
|
+
this.createWebSocket({
|
|
8176
|
+
attempt: tag,
|
|
8177
|
+
user_id: streamClient.user?.id || '',
|
|
8178
|
+
api_key: streamClient.key,
|
|
8179
|
+
user_session_id: this.sessionId,
|
|
8180
|
+
cid,
|
|
8181
|
+
});
|
|
8072
8182
|
}
|
|
8073
8183
|
get isHealthy() {
|
|
8074
8184
|
return (this.signalWs.readyState === WebSocket.OPEN &&
|
|
@@ -8228,6 +8338,36 @@ const watchCallGrantsUpdated = (state) => {
|
|
|
8228
8338
|
};
|
|
8229
8339
|
};
|
|
8230
8340
|
|
|
8341
|
+
/**
|
|
8342
|
+
* Adds unique values to an array.
|
|
8343
|
+
*
|
|
8344
|
+
* @param arr the array to add to.
|
|
8345
|
+
* @param values the values to add.
|
|
8346
|
+
*/
|
|
8347
|
+
const pushToIfMissing = (arr, ...values) => {
|
|
8348
|
+
for (const v of values) {
|
|
8349
|
+
if (!arr.includes(v)) {
|
|
8350
|
+
arr.push(v);
|
|
8351
|
+
}
|
|
8352
|
+
}
|
|
8353
|
+
return arr;
|
|
8354
|
+
};
|
|
8355
|
+
/**
|
|
8356
|
+
* Removes values from an array if they are present.
|
|
8357
|
+
*
|
|
8358
|
+
* @param arr the array to remove from.
|
|
8359
|
+
* @param values the values to remove.
|
|
8360
|
+
*/
|
|
8361
|
+
const removeFromIfPresent = (arr, ...values) => {
|
|
8362
|
+
for (const v of values) {
|
|
8363
|
+
const index = arr.indexOf(v);
|
|
8364
|
+
if (index !== -1) {
|
|
8365
|
+
arr.splice(index, 1);
|
|
8366
|
+
}
|
|
8367
|
+
}
|
|
8368
|
+
return arr;
|
|
8369
|
+
};
|
|
8370
|
+
|
|
8231
8371
|
const watchConnectionQualityChanged = (dispatcher, state) => {
|
|
8232
8372
|
return dispatcher.on('connectionQualityChanged', (e) => {
|
|
8233
8373
|
const { connectionQualityUpdates } = e;
|
|
@@ -8294,6 +8434,29 @@ const watchPinsUpdated = (state) => {
|
|
|
8294
8434
|
state.setServerSidePins(pins);
|
|
8295
8435
|
};
|
|
8296
8436
|
};
|
|
8437
|
+
/**
|
|
8438
|
+
* Watches for inbound state notifications and updates the paused tracks
|
|
8439
|
+
*
|
|
8440
|
+
* @param state the call state to update.
|
|
8441
|
+
*/
|
|
8442
|
+
const watchInboundStateNotification = (state) => {
|
|
8443
|
+
return function onInboundStateNotification(e) {
|
|
8444
|
+
const { inboundVideoStates } = e;
|
|
8445
|
+
const current = state.getParticipantLookupBySessionId();
|
|
8446
|
+
const patches = {};
|
|
8447
|
+
for (const { sessionId, trackType, paused } of inboundVideoStates) {
|
|
8448
|
+
const pausedTracks = [...(current[sessionId]?.pausedTracks ?? [])];
|
|
8449
|
+
if (paused) {
|
|
8450
|
+
pushToIfMissing(pausedTracks, trackType);
|
|
8451
|
+
}
|
|
8452
|
+
else {
|
|
8453
|
+
removeFromIfPresent(pausedTracks, trackType);
|
|
8454
|
+
}
|
|
8455
|
+
patches[sessionId] = { pausedTracks };
|
|
8456
|
+
}
|
|
8457
|
+
state.updateParticipants(patches);
|
|
8458
|
+
};
|
|
8459
|
+
};
|
|
8297
8460
|
|
|
8298
8461
|
/**
|
|
8299
8462
|
* An event handler that handles soft mutes.
|
|
@@ -8330,21 +8493,6 @@ const handleRemoteSoftMute = (call) => {
|
|
|
8330
8493
|
});
|
|
8331
8494
|
};
|
|
8332
8495
|
|
|
8333
|
-
/**
|
|
8334
|
-
* Adds unique values to an array.
|
|
8335
|
-
*
|
|
8336
|
-
* @param arr the array to add to.
|
|
8337
|
-
* @param values the values to add.
|
|
8338
|
-
*/
|
|
8339
|
-
const pushToIfMissing = (arr, ...values) => {
|
|
8340
|
-
for (const v of values) {
|
|
8341
|
-
if (!arr.includes(v)) {
|
|
8342
|
-
arr.push(v);
|
|
8343
|
-
}
|
|
8344
|
-
}
|
|
8345
|
-
return arr;
|
|
8346
|
-
};
|
|
8347
|
-
|
|
8348
8496
|
/**
|
|
8349
8497
|
* An event responder which handles the `participantJoined` event.
|
|
8350
8498
|
*/
|
|
@@ -8426,11 +8574,14 @@ const watchTrackUnpublished = (state) => {
|
|
|
8426
8574
|
if (e.participant) {
|
|
8427
8575
|
const orphanedTracks = reconcileOrphanedTracks(state, e.participant);
|
|
8428
8576
|
const participant = Object.assign(e.participant, orphanedTracks);
|
|
8429
|
-
state.updateOrAddParticipant(sessionId, participant)
|
|
8577
|
+
state.updateOrAddParticipant(sessionId, participant, (p) => ({
|
|
8578
|
+
pausedTracks: p.pausedTracks?.filter((t) => t !== type),
|
|
8579
|
+
}));
|
|
8430
8580
|
}
|
|
8431
8581
|
else {
|
|
8432
8582
|
state.updateParticipant(sessionId, (p) => ({
|
|
8433
8583
|
publishedTracks: p.publishedTracks.filter((t) => t !== type),
|
|
8584
|
+
pausedTracks: p.pausedTracks?.filter((t) => t !== type),
|
|
8434
8585
|
}));
|
|
8435
8586
|
}
|
|
8436
8587
|
};
|
|
@@ -8522,6 +8673,7 @@ const registerEventHandlers = (call, dispatcher) => {
|
|
|
8522
8673
|
watchDominantSpeakerChanged(dispatcher, state),
|
|
8523
8674
|
call.on('callGrantsUpdated', watchCallGrantsUpdated(state)),
|
|
8524
8675
|
call.on('pinsUpdated', watchPinsUpdated(state)),
|
|
8676
|
+
call.on('inboundStateNotification', watchInboundStateNotification(state)),
|
|
8525
8677
|
handleRemoteSoftMute(call),
|
|
8526
8678
|
];
|
|
8527
8679
|
if (call.ringing) {
|
|
@@ -11255,6 +11407,12 @@ class Call {
|
|
|
11255
11407
|
*/
|
|
11256
11408
|
this.leaveCallHooks = new Set();
|
|
11257
11409
|
this.streamClientEventHandlers = new Map();
|
|
11410
|
+
/**
|
|
11411
|
+
* A list of capabilities that the client supports and are enabled.
|
|
11412
|
+
*/
|
|
11413
|
+
this.clientCapabilities = new Set([
|
|
11414
|
+
ClientCapability.SUBSCRIBER_VIDEO_PAUSE,
|
|
11415
|
+
]);
|
|
11258
11416
|
/**
|
|
11259
11417
|
* Sets up the call instance.
|
|
11260
11418
|
*
|
|
@@ -11663,14 +11821,27 @@ class Call {
|
|
|
11663
11821
|
throw new Error(`Illegal State: call.join() shall be called only once`);
|
|
11664
11822
|
}
|
|
11665
11823
|
this.state.setCallingState(CallingState.JOINING);
|
|
11824
|
+
// we will count the number of join failures per SFU.
|
|
11825
|
+
// once the number of failures reaches 2, we will piggyback on the `migrating_from`
|
|
11826
|
+
// field to force the coordinator to provide us another SFU
|
|
11827
|
+
const sfuJoinFailures = new Map();
|
|
11828
|
+
const joinData = data;
|
|
11666
11829
|
maxJoinRetries = Math.max(maxJoinRetries, 1);
|
|
11667
11830
|
for (let attempt = 0; attempt < maxJoinRetries; attempt++) {
|
|
11668
11831
|
try {
|
|
11669
11832
|
this.logger('trace', `Joining call (${attempt})`, this.cid);
|
|
11670
|
-
|
|
11833
|
+
await this.doJoin(data);
|
|
11834
|
+
delete joinData.migrating_from;
|
|
11835
|
+
break;
|
|
11671
11836
|
}
|
|
11672
11837
|
catch (err) {
|
|
11673
11838
|
this.logger('warn', `Failed to join call (${attempt})`, this.cid);
|
|
11839
|
+
const sfuId = this.credentials?.server.edge_name || '';
|
|
11840
|
+
const failures = (sfuJoinFailures.get(sfuId) || 0) + 1;
|
|
11841
|
+
sfuJoinFailures.set(sfuId, failures);
|
|
11842
|
+
if (failures >= 2) {
|
|
11843
|
+
joinData.migrating_from = sfuId;
|
|
11844
|
+
}
|
|
11674
11845
|
if (attempt === maxJoinRetries - 1) {
|
|
11675
11846
|
// restore the previous call state if the join-flow fails
|
|
11676
11847
|
this.state.setCallingState(callingState);
|
|
@@ -11720,7 +11891,8 @@ class Call {
|
|
|
11720
11891
|
const isWsHealthy = !!previousSfuClient?.isHealthy;
|
|
11721
11892
|
const sfuClient = performingRejoin || performingMigration || !isWsHealthy
|
|
11722
11893
|
? new StreamSfuClient({
|
|
11723
|
-
|
|
11894
|
+
tag: String(this.sfuClientTag++),
|
|
11895
|
+
cid: this.cid,
|
|
11724
11896
|
dispatcher: this.dispatcher,
|
|
11725
11897
|
credentials: this.credentials,
|
|
11726
11898
|
streamClient: this.streamClient,
|
|
@@ -11762,6 +11934,7 @@ class Call {
|
|
|
11762
11934
|
reconnectDetails,
|
|
11763
11935
|
preferredPublishOptions,
|
|
11764
11936
|
preferredSubscribeOptions,
|
|
11937
|
+
capabilities: Array.from(this.clientCapabilities),
|
|
11765
11938
|
});
|
|
11766
11939
|
this.currentPublishOptions = publishOptions;
|
|
11767
11940
|
this.fastReconnectDeadlineSeconds = fastReconnectDeadlineSeconds;
|
|
@@ -11934,7 +12107,7 @@ class Call {
|
|
|
11934
12107
|
dispatcher: this.dispatcher,
|
|
11935
12108
|
state: this.state,
|
|
11936
12109
|
connectionConfig,
|
|
11937
|
-
|
|
12110
|
+
tag: sfuClient.tag,
|
|
11938
12111
|
enableTracing,
|
|
11939
12112
|
onReconnectionNeeded: (kind, reason) => {
|
|
11940
12113
|
this.reconnect(kind, reason).catch((err) => {
|
|
@@ -11956,7 +12129,7 @@ class Call {
|
|
|
11956
12129
|
state: this.state,
|
|
11957
12130
|
connectionConfig,
|
|
11958
12131
|
publishOptions,
|
|
11959
|
-
|
|
12132
|
+
tag: sfuClient.tag,
|
|
11960
12133
|
enableTracing,
|
|
11961
12134
|
onReconnectionNeeded: (kind, reason) => {
|
|
11962
12135
|
this.reconnect(kind, reason).catch((err) => {
|
|
@@ -13120,6 +13293,22 @@ class Call {
|
|
|
13120
13293
|
this.setDisconnectionTimeout = (timeoutSeconds) => {
|
|
13121
13294
|
this.disconnectionTimeoutSeconds = timeoutSeconds;
|
|
13122
13295
|
};
|
|
13296
|
+
/**
|
|
13297
|
+
* Enables the provided client capabilities.
|
|
13298
|
+
*/
|
|
13299
|
+
this.enableClientCapabilities = (...capabilities) => {
|
|
13300
|
+
for (const capability of capabilities) {
|
|
13301
|
+
this.clientCapabilities.add(capability);
|
|
13302
|
+
}
|
|
13303
|
+
};
|
|
13304
|
+
/**
|
|
13305
|
+
* Disables the provided client capabilities.
|
|
13306
|
+
*/
|
|
13307
|
+
this.disableClientCapabilities = (...capabilities) => {
|
|
13308
|
+
for (const capability of capabilities) {
|
|
13309
|
+
this.clientCapabilities.delete(capability);
|
|
13310
|
+
}
|
|
13311
|
+
};
|
|
13123
13312
|
this.type = type;
|
|
13124
13313
|
this.id = id;
|
|
13125
13314
|
this.cid = `${type}:${id}`;
|
|
@@ -13287,7 +13476,6 @@ class StableWSConnection {
|
|
|
13287
13476
|
this.onmessage = (wsID, event) => {
|
|
13288
13477
|
if (this.wsID !== wsID)
|
|
13289
13478
|
return;
|
|
13290
|
-
this._log('onmessage() - onmessage callback', { event, wsID });
|
|
13291
13479
|
const data = typeof event.data === 'string'
|
|
13292
13480
|
? JSON.parse(event.data)
|
|
13293
13481
|
: null;
|
|
@@ -14301,7 +14489,7 @@ class StreamClient {
|
|
|
14301
14489
|
this.getUserAgent = () => {
|
|
14302
14490
|
if (!this.cachedUserAgent) {
|
|
14303
14491
|
const { clientAppIdentifier = {} } = this.options;
|
|
14304
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
14492
|
+
const { sdkName = 'js', sdkVersion = "1.27.0", ...extras } = clientAppIdentifier;
|
|
14305
14493
|
this.cachedUserAgent = [
|
|
14306
14494
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
14307
14495
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -14854,5 +15042,5 @@ class StreamVideoClient {
|
|
|
14854
15042
|
}
|
|
14855
15043
|
StreamVideoClient._instances = new Map();
|
|
14856
15044
|
|
|
14857
|
-
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, InputMediaDeviceManager, InputMediaDeviceManagerState, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RNSpeechDetector, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getLogLevel, getLogger, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio, hasScreenShare, hasScreenShareAudio, hasVideo, isPinned, livestreamOrAudioRoomSortPreset, logLevels, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setLogLevel, setLogger, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking };
|
|
15045
|
+
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, InputMediaDeviceManager, InputMediaDeviceManagerState, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RNSpeechDetector, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getLogLevel, getLogger, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, isPinned, livestreamOrAudioRoomSortPreset, logLevels, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setLogLevel, setLogger, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking };
|
|
14858
15046
|
//# sourceMappingURL=index.browser.es.js.map
|