@stream-io/video-client 1.26.0 → 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 +16 -0
- package/dist/index.browser.es.js +241 -47
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +241 -46
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +241 -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/DynascaleManager.ts +6 -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/dist/index.es.js
CHANGED
|
@@ -972,6 +972,10 @@ var ErrorCode;
|
|
|
972
972
|
* @generated from protobuf enum value: ERROR_CODE_CALL_NOT_FOUND = 300;
|
|
973
973
|
*/
|
|
974
974
|
ErrorCode[ErrorCode["CALL_NOT_FOUND"] = 300] = "CALL_NOT_FOUND";
|
|
975
|
+
/**
|
|
976
|
+
* @generated from protobuf enum value: ERROR_CODE_CALL_PARTICIPANT_LIMIT_REACHED = 301;
|
|
977
|
+
*/
|
|
978
|
+
ErrorCode[ErrorCode["CALL_PARTICIPANT_LIMIT_REACHED"] = 301] = "CALL_PARTICIPANT_LIMIT_REACHED";
|
|
975
979
|
/**
|
|
976
980
|
* @generated from protobuf enum value: ERROR_CODE_REQUEST_VALIDATION_FAILED = 400;
|
|
977
981
|
*/
|
|
@@ -1240,6 +1244,24 @@ var AppleThermalState;
|
|
|
1240
1244
|
*/
|
|
1241
1245
|
AppleThermalState[AppleThermalState["CRITICAL"] = 4] = "CRITICAL";
|
|
1242
1246
|
})(AppleThermalState || (AppleThermalState = {}));
|
|
1247
|
+
/**
|
|
1248
|
+
* ClientCapability defines a feature that client supports
|
|
1249
|
+
*
|
|
1250
|
+
* @generated from protobuf enum stream.video.sfu.models.ClientCapability
|
|
1251
|
+
*/
|
|
1252
|
+
var ClientCapability;
|
|
1253
|
+
(function (ClientCapability) {
|
|
1254
|
+
/**
|
|
1255
|
+
* @generated from protobuf enum value: CLIENT_CAPABILITY_UNSPECIFIED = 0;
|
|
1256
|
+
*/
|
|
1257
|
+
ClientCapability[ClientCapability["UNSPECIFIED"] = 0] = "UNSPECIFIED";
|
|
1258
|
+
/**
|
|
1259
|
+
* Enables SFU pausing inbound video
|
|
1260
|
+
*
|
|
1261
|
+
* @generated from protobuf enum value: CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE = 1;
|
|
1262
|
+
*/
|
|
1263
|
+
ClientCapability[ClientCapability["SUBSCRIBER_VIDEO_PAUSE"] = 1] = "SUBSCRIBER_VIDEO_PAUSE";
|
|
1264
|
+
})(ClientCapability || (ClientCapability = {}));
|
|
1243
1265
|
// @generated message type with reflection information, may provide speed optimized methods
|
|
1244
1266
|
class CallState$Type extends MessageType {
|
|
1245
1267
|
constructor() {
|
|
@@ -1910,6 +1932,7 @@ var models = /*#__PURE__*/Object.freeze({
|
|
|
1910
1932
|
get CallEndedReason () { return CallEndedReason; },
|
|
1911
1933
|
CallGrants: CallGrants,
|
|
1912
1934
|
CallState: CallState$1,
|
|
1935
|
+
get ClientCapability () { return ClientCapability; },
|
|
1913
1936
|
ClientDetails: ClientDetails,
|
|
1914
1937
|
Codec: Codec,
|
|
1915
1938
|
get ConnectionQuality () { return ConnectionQuality; },
|
|
@@ -2599,6 +2622,13 @@ class SfuEvent$Type extends MessageType {
|
|
|
2599
2622
|
oneof: 'eventPayload',
|
|
2600
2623
|
T: () => ChangePublishOptions,
|
|
2601
2624
|
},
|
|
2625
|
+
{
|
|
2626
|
+
no: 28,
|
|
2627
|
+
name: 'inbound_state_notification',
|
|
2628
|
+
kind: 'message',
|
|
2629
|
+
oneof: 'eventPayload',
|
|
2630
|
+
T: () => InboundStateNotification,
|
|
2631
|
+
},
|
|
2602
2632
|
]);
|
|
2603
2633
|
}
|
|
2604
2634
|
}
|
|
@@ -2906,6 +2936,17 @@ class JoinRequest$Type extends MessageType {
|
|
|
2906
2936
|
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
2907
2937
|
T: () => SubscribeOption,
|
|
2908
2938
|
},
|
|
2939
|
+
{
|
|
2940
|
+
no: 11,
|
|
2941
|
+
name: 'capabilities',
|
|
2942
|
+
kind: 'enum',
|
|
2943
|
+
repeat: 1 /*RepeatType.PACKED*/,
|
|
2944
|
+
T: () => [
|
|
2945
|
+
'stream.video.sfu.models.ClientCapability',
|
|
2946
|
+
ClientCapability,
|
|
2947
|
+
'CLIENT_CAPABILITY_',
|
|
2948
|
+
],
|
|
2949
|
+
},
|
|
2909
2950
|
]);
|
|
2910
2951
|
}
|
|
2911
2952
|
}
|
|
@@ -3351,6 +3392,48 @@ class CallEnded$Type extends MessageType {
|
|
|
3351
3392
|
* @generated MessageType for protobuf message stream.video.sfu.event.CallEnded
|
|
3352
3393
|
*/
|
|
3353
3394
|
const CallEnded = new CallEnded$Type();
|
|
3395
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
3396
|
+
class InboundStateNotification$Type extends MessageType {
|
|
3397
|
+
constructor() {
|
|
3398
|
+
super('stream.video.sfu.event.InboundStateNotification', [
|
|
3399
|
+
{
|
|
3400
|
+
no: 1,
|
|
3401
|
+
name: 'inbound_video_states',
|
|
3402
|
+
kind: 'message',
|
|
3403
|
+
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
3404
|
+
T: () => InboundVideoState,
|
|
3405
|
+
},
|
|
3406
|
+
]);
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
/**
|
|
3410
|
+
* @generated MessageType for protobuf message stream.video.sfu.event.InboundStateNotification
|
|
3411
|
+
*/
|
|
3412
|
+
const InboundStateNotification = new InboundStateNotification$Type();
|
|
3413
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
3414
|
+
class InboundVideoState$Type extends MessageType {
|
|
3415
|
+
constructor() {
|
|
3416
|
+
super('stream.video.sfu.event.InboundVideoState', [
|
|
3417
|
+
{ no: 1, name: 'user_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
3418
|
+
{ no: 2, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
3419
|
+
{
|
|
3420
|
+
no: 3,
|
|
3421
|
+
name: 'track_type',
|
|
3422
|
+
kind: 'enum',
|
|
3423
|
+
T: () => [
|
|
3424
|
+
'stream.video.sfu.models.TrackType',
|
|
3425
|
+
TrackType,
|
|
3426
|
+
'TRACK_TYPE_',
|
|
3427
|
+
],
|
|
3428
|
+
},
|
|
3429
|
+
{ no: 4, name: 'paused', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
|
|
3430
|
+
]);
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
/**
|
|
3434
|
+
* @generated MessageType for protobuf message stream.video.sfu.event.InboundVideoState
|
|
3435
|
+
*/
|
|
3436
|
+
const InboundVideoState = new InboundVideoState$Type();
|
|
3354
3437
|
|
|
3355
3438
|
var events = /*#__PURE__*/Object.freeze({
|
|
3356
3439
|
__proto__: null,
|
|
@@ -3371,6 +3454,8 @@ var events = /*#__PURE__*/Object.freeze({
|
|
|
3371
3454
|
HealthCheckResponse: HealthCheckResponse,
|
|
3372
3455
|
ICERestart: ICERestart,
|
|
3373
3456
|
ICETrickle: ICETrickle,
|
|
3457
|
+
InboundStateNotification: InboundStateNotification,
|
|
3458
|
+
InboundVideoState: InboundVideoState,
|
|
3374
3459
|
JoinRequest: JoinRequest,
|
|
3375
3460
|
JoinResponse: JoinResponse,
|
|
3376
3461
|
LeaveCallRequest: LeaveCallRequest,
|
|
@@ -3795,6 +3880,7 @@ const sfuEventKinds = {
|
|
|
3795
3880
|
participantUpdated: undefined,
|
|
3796
3881
|
participantMigrationComplete: undefined,
|
|
3797
3882
|
changePublishOptions: undefined,
|
|
3883
|
+
inboundStateNotification: undefined,
|
|
3798
3884
|
};
|
|
3799
3885
|
const isSfuEvent = (eventName) => {
|
|
3800
3886
|
return Object.prototype.hasOwnProperty.call(sfuEventKinds, eventName);
|
|
@@ -3803,12 +3889,12 @@ class Dispatcher {
|
|
|
3803
3889
|
constructor() {
|
|
3804
3890
|
this.logger = getLogger(['Dispatcher']);
|
|
3805
3891
|
this.subscribers = {};
|
|
3806
|
-
this.dispatch = (message,
|
|
3892
|
+
this.dispatch = (message, tag = '0') => {
|
|
3807
3893
|
const eventKind = message.eventPayload.oneofKind;
|
|
3808
3894
|
if (!eventKind)
|
|
3809
3895
|
return;
|
|
3810
3896
|
const payload = message.eventPayload[eventKind];
|
|
3811
|
-
this.logger('debug', `Dispatching ${eventKind}, tag=${
|
|
3897
|
+
this.logger('debug', `Dispatching ${eventKind}, tag=${tag}`, payload);
|
|
3812
3898
|
const listeners = this.subscribers[eventKind];
|
|
3813
3899
|
if (!listeners)
|
|
3814
3900
|
return;
|
|
@@ -4325,6 +4411,24 @@ const hasScreenShareAudio = (p) => p.publishedTracks.includes(TrackType.SCREEN_S
|
|
|
4325
4411
|
* @param p the participant.
|
|
4326
4412
|
*/
|
|
4327
4413
|
const isPinned = (p) => !!p.pin && (p.pin.isLocalPin || p.pin.pinnedAt > 0);
|
|
4414
|
+
/**
|
|
4415
|
+
* Check if a participant has a paused track of the specified type.
|
|
4416
|
+
*
|
|
4417
|
+
* @param p the participant to check.
|
|
4418
|
+
* @param videoTrackType the type of video track to check for ('videoTrack' or 'screenShareTrack').
|
|
4419
|
+
*/
|
|
4420
|
+
const hasPausedTrack = (p, videoTrackType) => {
|
|
4421
|
+
if (!p.pausedTracks)
|
|
4422
|
+
return false;
|
|
4423
|
+
const trackType = videoTrackType === 'videoTrack'
|
|
4424
|
+
? TrackType.VIDEO
|
|
4425
|
+
: videoTrackType === 'screenShareTrack'
|
|
4426
|
+
? TrackType.SCREEN_SHARE
|
|
4427
|
+
: undefined;
|
|
4428
|
+
if (!trackType)
|
|
4429
|
+
return false;
|
|
4430
|
+
return p.pausedTracks.includes(trackType);
|
|
4431
|
+
};
|
|
4328
4432
|
|
|
4329
4433
|
/**
|
|
4330
4434
|
* A comparator which sorts participants by the fact that they are the dominant speaker or not.
|
|
@@ -4727,17 +4831,17 @@ class CallState {
|
|
|
4727
4831
|
*
|
|
4728
4832
|
* @param sessionId the session ID of the participant to update.
|
|
4729
4833
|
* @param participant the participant to update or add.
|
|
4834
|
+
* @param patch an optional patch to apply to the participant.
|
|
4730
4835
|
*/
|
|
4731
|
-
this.updateOrAddParticipant = (sessionId, participant) => {
|
|
4836
|
+
this.updateOrAddParticipant = (sessionId, participant, patch) => {
|
|
4732
4837
|
return this.setParticipants((participants) => {
|
|
4733
4838
|
let add = true;
|
|
4734
4839
|
const nextParticipants = participants.map((p) => {
|
|
4735
4840
|
if (p.sessionId === sessionId) {
|
|
4736
4841
|
add = false;
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
};
|
|
4842
|
+
const updated = { ...p, ...participant };
|
|
4843
|
+
const thePatch = typeof patch === 'function' ? patch(updated) : patch;
|
|
4844
|
+
return Object.assign(updated, thePatch);
|
|
4741
4845
|
}
|
|
4742
4846
|
return p;
|
|
4743
4847
|
});
|
|
@@ -5811,7 +5915,7 @@ const aggregate = (stats) => {
|
|
|
5811
5915
|
return report;
|
|
5812
5916
|
};
|
|
5813
5917
|
|
|
5814
|
-
const version = "1.
|
|
5918
|
+
const version = "1.27.0";
|
|
5815
5919
|
const [major, minor, patch] = version.split('.');
|
|
5816
5920
|
let sdkInfo = {
|
|
5817
5921
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -6420,7 +6524,7 @@ class BasePeerConnection {
|
|
|
6420
6524
|
/**
|
|
6421
6525
|
* Constructs a new `BasePeerConnection` instance.
|
|
6422
6526
|
*/
|
|
6423
|
-
constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded,
|
|
6527
|
+
constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, tag, enableTracing, iceRestartDelay = 2500, }) {
|
|
6424
6528
|
this.isIceRestarting = false;
|
|
6425
6529
|
this.isDisposed = false;
|
|
6426
6530
|
this.trackIdToTrackType = new Map();
|
|
@@ -6644,13 +6748,12 @@ class BasePeerConnection {
|
|
|
6644
6748
|
this.onReconnectionNeeded = onReconnectionNeeded;
|
|
6645
6749
|
this.logger = getLogger([
|
|
6646
6750
|
peerType === PeerType.SUBSCRIBER ? 'Subscriber' : 'Publisher',
|
|
6647
|
-
|
|
6751
|
+
tag,
|
|
6648
6752
|
]);
|
|
6649
6753
|
this.pc = this.createPeerConnection(connectionConfig);
|
|
6650
6754
|
this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType);
|
|
6651
6755
|
if (enableTracing) {
|
|
6652
|
-
|
|
6653
|
-
this.tracer = new Tracer(tag);
|
|
6756
|
+
this.tracer = new Tracer(`${tag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}`);
|
|
6654
6757
|
this.tracer.trace('create', {
|
|
6655
6758
|
url: sfuClient.edgeName,
|
|
6656
6759
|
...connectionConfig,
|
|
@@ -7522,8 +7625,8 @@ class Subscriber extends BasePeerConnection {
|
|
|
7522
7625
|
}
|
|
7523
7626
|
|
|
7524
7627
|
const createWebSocketSignalChannel = (opts) => {
|
|
7525
|
-
const { endpoint, onMessage,
|
|
7526
|
-
const logger = getLogger(['SfuClientWS',
|
|
7628
|
+
const { endpoint, onMessage, tag } = opts;
|
|
7629
|
+
const logger = getLogger(['SfuClientWS', tag]);
|
|
7527
7630
|
logger('debug', 'Creating signaling WS channel:', endpoint);
|
|
7528
7631
|
const ws = new WebSocket(endpoint);
|
|
7529
7632
|
ws.binaryType = 'arraybuffer'; // do we need this?
|
|
@@ -7773,7 +7876,7 @@ class StreamSfuClient {
|
|
|
7773
7876
|
/**
|
|
7774
7877
|
* Constructs a new SFU client.
|
|
7775
7878
|
*/
|
|
7776
|
-
constructor({ dispatcher, credentials, sessionId,
|
|
7879
|
+
constructor({ dispatcher, credentials, sessionId, cid, tag, joinResponseTimeout = 5000, onSignalClose, streamClient, enableTracing, }) {
|
|
7777
7880
|
/**
|
|
7778
7881
|
* A buffer for ICE Candidates that are received before
|
|
7779
7882
|
* the Publisher and Subscriber Peer Connections are ready to handle them.
|
|
@@ -7803,7 +7906,7 @@ class StreamSfuClient {
|
|
|
7803
7906
|
* A controller to abort the current requests.
|
|
7804
7907
|
*/
|
|
7805
7908
|
this.abortController = new AbortController();
|
|
7806
|
-
this.createWebSocket = () => {
|
|
7909
|
+
this.createWebSocket = (params) => {
|
|
7807
7910
|
const eventsToTrace = {
|
|
7808
7911
|
callEnded: true,
|
|
7809
7912
|
changePublishQuality: true,
|
|
@@ -7811,10 +7914,11 @@ class StreamSfuClient {
|
|
|
7811
7914
|
connectionQualityChanged: true,
|
|
7812
7915
|
error: true,
|
|
7813
7916
|
goAway: true,
|
|
7917
|
+
inboundStateNotification: true,
|
|
7814
7918
|
};
|
|
7815
7919
|
this.signalWs = createWebSocketSignalChannel({
|
|
7816
|
-
|
|
7817
|
-
endpoint: `${this.credentials.server.ws_endpoint}
|
|
7920
|
+
tag: this.tag,
|
|
7921
|
+
endpoint: `${this.credentials.server.ws_endpoint}?${new URLSearchParams(params).toString()}`,
|
|
7818
7922
|
onMessage: (message) => {
|
|
7819
7923
|
this.lastMessageTimestamp = new Date();
|
|
7820
7924
|
this.scheduleConnectionCheck();
|
|
@@ -7822,7 +7926,7 @@ class StreamSfuClient {
|
|
|
7822
7926
|
if (eventsToTrace[eventKind]) {
|
|
7823
7927
|
this.tracer?.trace(eventKind, message);
|
|
7824
7928
|
}
|
|
7825
|
-
this.dispatcher.dispatch(message, this.
|
|
7929
|
+
this.dispatcher.dispatch(message, this.tag);
|
|
7826
7930
|
},
|
|
7827
7931
|
});
|
|
7828
7932
|
this.signalReady = makeSafePromise(Promise.race([
|
|
@@ -7937,7 +8041,7 @@ class StreamSfuClient {
|
|
|
7937
8041
|
});
|
|
7938
8042
|
this.migrateAwayTimeout = setTimeout(() => {
|
|
7939
8043
|
unsubscribe();
|
|
7940
|
-
task.reject(new Error(`Migration (${this.
|
|
8044
|
+
task.reject(new Error(`Migration (${this.tag}) failed to complete in ${timeout}ms`));
|
|
7941
8045
|
}, timeout);
|
|
7942
8046
|
return task.promise;
|
|
7943
8047
|
};
|
|
@@ -8037,10 +8141,10 @@ class StreamSfuClient {
|
|
|
8037
8141
|
const { server, token } = credentials;
|
|
8038
8142
|
this.edgeName = server.edge_name;
|
|
8039
8143
|
this.joinResponseTimeout = joinResponseTimeout;
|
|
8040
|
-
this.
|
|
8041
|
-
this.logger = getLogger(['SfuClient',
|
|
8144
|
+
this.tag = tag;
|
|
8145
|
+
this.logger = getLogger(['SfuClient', tag]);
|
|
8042
8146
|
this.tracer = enableTracing
|
|
8043
|
-
? new Tracer(`${
|
|
8147
|
+
? new Tracer(`${tag}-${this.edgeName}`)
|
|
8044
8148
|
: undefined;
|
|
8045
8149
|
this.rpc = createSignalClient({
|
|
8046
8150
|
baseUrl: server.url,
|
|
@@ -8069,7 +8173,13 @@ class StreamSfuClient {
|
|
|
8069
8173
|
this.networkAvailableTask?.resolve();
|
|
8070
8174
|
}
|
|
8071
8175
|
});
|
|
8072
|
-
this.createWebSocket(
|
|
8176
|
+
this.createWebSocket({
|
|
8177
|
+
attempt: tag,
|
|
8178
|
+
user_id: streamClient.user?.id || '',
|
|
8179
|
+
api_key: streamClient.key,
|
|
8180
|
+
user_session_id: this.sessionId,
|
|
8181
|
+
cid,
|
|
8182
|
+
});
|
|
8073
8183
|
}
|
|
8074
8184
|
get isHealthy() {
|
|
8075
8185
|
return (this.signalWs.readyState === WebSocket.OPEN &&
|
|
@@ -8229,6 +8339,36 @@ const watchCallGrantsUpdated = (state) => {
|
|
|
8229
8339
|
};
|
|
8230
8340
|
};
|
|
8231
8341
|
|
|
8342
|
+
/**
|
|
8343
|
+
* Adds unique values to an array.
|
|
8344
|
+
*
|
|
8345
|
+
* @param arr the array to add to.
|
|
8346
|
+
* @param values the values to add.
|
|
8347
|
+
*/
|
|
8348
|
+
const pushToIfMissing = (arr, ...values) => {
|
|
8349
|
+
for (const v of values) {
|
|
8350
|
+
if (!arr.includes(v)) {
|
|
8351
|
+
arr.push(v);
|
|
8352
|
+
}
|
|
8353
|
+
}
|
|
8354
|
+
return arr;
|
|
8355
|
+
};
|
|
8356
|
+
/**
|
|
8357
|
+
* Removes values from an array if they are present.
|
|
8358
|
+
*
|
|
8359
|
+
* @param arr the array to remove from.
|
|
8360
|
+
* @param values the values to remove.
|
|
8361
|
+
*/
|
|
8362
|
+
const removeFromIfPresent = (arr, ...values) => {
|
|
8363
|
+
for (const v of values) {
|
|
8364
|
+
const index = arr.indexOf(v);
|
|
8365
|
+
if (index !== -1) {
|
|
8366
|
+
arr.splice(index, 1);
|
|
8367
|
+
}
|
|
8368
|
+
}
|
|
8369
|
+
return arr;
|
|
8370
|
+
};
|
|
8371
|
+
|
|
8232
8372
|
const watchConnectionQualityChanged = (dispatcher, state) => {
|
|
8233
8373
|
return dispatcher.on('connectionQualityChanged', (e) => {
|
|
8234
8374
|
const { connectionQualityUpdates } = e;
|
|
@@ -8295,6 +8435,29 @@ const watchPinsUpdated = (state) => {
|
|
|
8295
8435
|
state.setServerSidePins(pins);
|
|
8296
8436
|
};
|
|
8297
8437
|
};
|
|
8438
|
+
/**
|
|
8439
|
+
* Watches for inbound state notifications and updates the paused tracks
|
|
8440
|
+
*
|
|
8441
|
+
* @param state the call state to update.
|
|
8442
|
+
*/
|
|
8443
|
+
const watchInboundStateNotification = (state) => {
|
|
8444
|
+
return function onInboundStateNotification(e) {
|
|
8445
|
+
const { inboundVideoStates } = e;
|
|
8446
|
+
const current = state.getParticipantLookupBySessionId();
|
|
8447
|
+
const patches = {};
|
|
8448
|
+
for (const { sessionId, trackType, paused } of inboundVideoStates) {
|
|
8449
|
+
const pausedTracks = [...(current[sessionId]?.pausedTracks ?? [])];
|
|
8450
|
+
if (paused) {
|
|
8451
|
+
pushToIfMissing(pausedTracks, trackType);
|
|
8452
|
+
}
|
|
8453
|
+
else {
|
|
8454
|
+
removeFromIfPresent(pausedTracks, trackType);
|
|
8455
|
+
}
|
|
8456
|
+
patches[sessionId] = { pausedTracks };
|
|
8457
|
+
}
|
|
8458
|
+
state.updateParticipants(patches);
|
|
8459
|
+
};
|
|
8460
|
+
};
|
|
8298
8461
|
|
|
8299
8462
|
/**
|
|
8300
8463
|
* An event handler that handles soft mutes.
|
|
@@ -8331,21 +8494,6 @@ const handleRemoteSoftMute = (call) => {
|
|
|
8331
8494
|
});
|
|
8332
8495
|
};
|
|
8333
8496
|
|
|
8334
|
-
/**
|
|
8335
|
-
* Adds unique values to an array.
|
|
8336
|
-
*
|
|
8337
|
-
* @param arr the array to add to.
|
|
8338
|
-
* @param values the values to add.
|
|
8339
|
-
*/
|
|
8340
|
-
const pushToIfMissing = (arr, ...values) => {
|
|
8341
|
-
for (const v of values) {
|
|
8342
|
-
if (!arr.includes(v)) {
|
|
8343
|
-
arr.push(v);
|
|
8344
|
-
}
|
|
8345
|
-
}
|
|
8346
|
-
return arr;
|
|
8347
|
-
};
|
|
8348
|
-
|
|
8349
8497
|
/**
|
|
8350
8498
|
* An event responder which handles the `participantJoined` event.
|
|
8351
8499
|
*/
|
|
@@ -8427,11 +8575,14 @@ const watchTrackUnpublished = (state) => {
|
|
|
8427
8575
|
if (e.participant) {
|
|
8428
8576
|
const orphanedTracks = reconcileOrphanedTracks(state, e.participant);
|
|
8429
8577
|
const participant = Object.assign(e.participant, orphanedTracks);
|
|
8430
|
-
state.updateOrAddParticipant(sessionId, participant)
|
|
8578
|
+
state.updateOrAddParticipant(sessionId, participant, (p) => ({
|
|
8579
|
+
pausedTracks: p.pausedTracks?.filter((t) => t !== type),
|
|
8580
|
+
}));
|
|
8431
8581
|
}
|
|
8432
8582
|
else {
|
|
8433
8583
|
state.updateParticipant(sessionId, (p) => ({
|
|
8434
8584
|
publishedTracks: p.publishedTracks.filter((t) => t !== type),
|
|
8585
|
+
pausedTracks: p.pausedTracks?.filter((t) => t !== type),
|
|
8435
8586
|
}));
|
|
8436
8587
|
}
|
|
8437
8588
|
};
|
|
@@ -8523,6 +8674,7 @@ const registerEventHandlers = (call, dispatcher) => {
|
|
|
8523
8674
|
watchDominantSpeakerChanged(dispatcher, state),
|
|
8524
8675
|
call.on('callGrantsUpdated', watchCallGrantsUpdated(state)),
|
|
8525
8676
|
call.on('pinsUpdated', watchPinsUpdated(state)),
|
|
8677
|
+
call.on('inboundStateNotification', watchInboundStateNotification(state)),
|
|
8526
8678
|
handleRemoteSoftMute(call),
|
|
8527
8679
|
];
|
|
8528
8680
|
if (call.ringing) {
|
|
@@ -9037,6 +9189,12 @@ class DynascaleManager {
|
|
|
9037
9189
|
if (context.state === 'suspended') {
|
|
9038
9190
|
document.addEventListener('click', this.resumeAudioContext);
|
|
9039
9191
|
}
|
|
9192
|
+
// @ts-expect-error audioSession is available in Safari only
|
|
9193
|
+
const audioSession = navigator.audioSession;
|
|
9194
|
+
if (audioSession) {
|
|
9195
|
+
// https://github.com/w3c/audio-session/blob/main/explainer.md
|
|
9196
|
+
audioSession.type = 'play-and-record';
|
|
9197
|
+
}
|
|
9040
9198
|
return (this.audioContext = context);
|
|
9041
9199
|
};
|
|
9042
9200
|
this.resumeAudioContext = () => {
|
|
@@ -11250,6 +11408,12 @@ class Call {
|
|
|
11250
11408
|
*/
|
|
11251
11409
|
this.leaveCallHooks = new Set();
|
|
11252
11410
|
this.streamClientEventHandlers = new Map();
|
|
11411
|
+
/**
|
|
11412
|
+
* A list of capabilities that the client supports and are enabled.
|
|
11413
|
+
*/
|
|
11414
|
+
this.clientCapabilities = new Set([
|
|
11415
|
+
ClientCapability.SUBSCRIBER_VIDEO_PAUSE,
|
|
11416
|
+
]);
|
|
11253
11417
|
/**
|
|
11254
11418
|
* Sets up the call instance.
|
|
11255
11419
|
*
|
|
@@ -11658,14 +11822,27 @@ class Call {
|
|
|
11658
11822
|
throw new Error(`Illegal State: call.join() shall be called only once`);
|
|
11659
11823
|
}
|
|
11660
11824
|
this.state.setCallingState(CallingState.JOINING);
|
|
11825
|
+
// we will count the number of join failures per SFU.
|
|
11826
|
+
// once the number of failures reaches 2, we will piggyback on the `migrating_from`
|
|
11827
|
+
// field to force the coordinator to provide us another SFU
|
|
11828
|
+
const sfuJoinFailures = new Map();
|
|
11829
|
+
const joinData = data;
|
|
11661
11830
|
maxJoinRetries = Math.max(maxJoinRetries, 1);
|
|
11662
11831
|
for (let attempt = 0; attempt < maxJoinRetries; attempt++) {
|
|
11663
11832
|
try {
|
|
11664
11833
|
this.logger('trace', `Joining call (${attempt})`, this.cid);
|
|
11665
|
-
|
|
11834
|
+
await this.doJoin(data);
|
|
11835
|
+
delete joinData.migrating_from;
|
|
11836
|
+
break;
|
|
11666
11837
|
}
|
|
11667
11838
|
catch (err) {
|
|
11668
11839
|
this.logger('warn', `Failed to join call (${attempt})`, this.cid);
|
|
11840
|
+
const sfuId = this.credentials?.server.edge_name || '';
|
|
11841
|
+
const failures = (sfuJoinFailures.get(sfuId) || 0) + 1;
|
|
11842
|
+
sfuJoinFailures.set(sfuId, failures);
|
|
11843
|
+
if (failures >= 2) {
|
|
11844
|
+
joinData.migrating_from = sfuId;
|
|
11845
|
+
}
|
|
11669
11846
|
if (attempt === maxJoinRetries - 1) {
|
|
11670
11847
|
// restore the previous call state if the join-flow fails
|
|
11671
11848
|
this.state.setCallingState(callingState);
|
|
@@ -11715,7 +11892,8 @@ class Call {
|
|
|
11715
11892
|
const isWsHealthy = !!previousSfuClient?.isHealthy;
|
|
11716
11893
|
const sfuClient = performingRejoin || performingMigration || !isWsHealthy
|
|
11717
11894
|
? new StreamSfuClient({
|
|
11718
|
-
|
|
11895
|
+
tag: String(this.sfuClientTag++),
|
|
11896
|
+
cid: this.cid,
|
|
11719
11897
|
dispatcher: this.dispatcher,
|
|
11720
11898
|
credentials: this.credentials,
|
|
11721
11899
|
streamClient: this.streamClient,
|
|
@@ -11757,6 +11935,7 @@ class Call {
|
|
|
11757
11935
|
reconnectDetails,
|
|
11758
11936
|
preferredPublishOptions,
|
|
11759
11937
|
preferredSubscribeOptions,
|
|
11938
|
+
capabilities: Array.from(this.clientCapabilities),
|
|
11760
11939
|
});
|
|
11761
11940
|
this.currentPublishOptions = publishOptions;
|
|
11762
11941
|
this.fastReconnectDeadlineSeconds = fastReconnectDeadlineSeconds;
|
|
@@ -11929,7 +12108,7 @@ class Call {
|
|
|
11929
12108
|
dispatcher: this.dispatcher,
|
|
11930
12109
|
state: this.state,
|
|
11931
12110
|
connectionConfig,
|
|
11932
|
-
|
|
12111
|
+
tag: sfuClient.tag,
|
|
11933
12112
|
enableTracing,
|
|
11934
12113
|
onReconnectionNeeded: (kind, reason) => {
|
|
11935
12114
|
this.reconnect(kind, reason).catch((err) => {
|
|
@@ -11951,7 +12130,7 @@ class Call {
|
|
|
11951
12130
|
state: this.state,
|
|
11952
12131
|
connectionConfig,
|
|
11953
12132
|
publishOptions,
|
|
11954
|
-
|
|
12133
|
+
tag: sfuClient.tag,
|
|
11955
12134
|
enableTracing,
|
|
11956
12135
|
onReconnectionNeeded: (kind, reason) => {
|
|
11957
12136
|
this.reconnect(kind, reason).catch((err) => {
|
|
@@ -13115,6 +13294,22 @@ class Call {
|
|
|
13115
13294
|
this.setDisconnectionTimeout = (timeoutSeconds) => {
|
|
13116
13295
|
this.disconnectionTimeoutSeconds = timeoutSeconds;
|
|
13117
13296
|
};
|
|
13297
|
+
/**
|
|
13298
|
+
* Enables the provided client capabilities.
|
|
13299
|
+
*/
|
|
13300
|
+
this.enableClientCapabilities = (...capabilities) => {
|
|
13301
|
+
for (const capability of capabilities) {
|
|
13302
|
+
this.clientCapabilities.add(capability);
|
|
13303
|
+
}
|
|
13304
|
+
};
|
|
13305
|
+
/**
|
|
13306
|
+
* Disables the provided client capabilities.
|
|
13307
|
+
*/
|
|
13308
|
+
this.disableClientCapabilities = (...capabilities) => {
|
|
13309
|
+
for (const capability of capabilities) {
|
|
13310
|
+
this.clientCapabilities.delete(capability);
|
|
13311
|
+
}
|
|
13312
|
+
};
|
|
13118
13313
|
this.type = type;
|
|
13119
13314
|
this.id = id;
|
|
13120
13315
|
this.cid = `${type}:${id}`;
|
|
@@ -13280,7 +13475,6 @@ class StableWSConnection {
|
|
|
13280
13475
|
this.onmessage = (wsID, event) => {
|
|
13281
13476
|
if (this.wsID !== wsID)
|
|
13282
13477
|
return;
|
|
13283
|
-
this._log('onmessage() - onmessage callback', { event, wsID });
|
|
13284
13478
|
const data = typeof event.data === 'string'
|
|
13285
13479
|
? JSON.parse(event.data)
|
|
13286
13480
|
: null;
|
|
@@ -14294,7 +14488,7 @@ class StreamClient {
|
|
|
14294
14488
|
this.getUserAgent = () => {
|
|
14295
14489
|
if (!this.cachedUserAgent) {
|
|
14296
14490
|
const { clientAppIdentifier = {} } = this.options;
|
|
14297
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
14491
|
+
const { sdkName = 'js', sdkVersion = "1.27.0", ...extras } = clientAppIdentifier;
|
|
14298
14492
|
this.cachedUserAgent = [
|
|
14299
14493
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
14300
14494
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -14847,5 +15041,5 @@ class StreamVideoClient {
|
|
|
14847
15041
|
}
|
|
14848
15042
|
StreamVideoClient._instances = new Map();
|
|
14849
15043
|
|
|
14850
|
-
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 };
|
|
15044
|
+
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 };
|
|
14851
15045
|
//# sourceMappingURL=index.es.js.map
|