@stream-io/video-client 1.44.6-beta.0 → 1.45.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 +58 -79
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +58 -79
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +58 -79
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +1 -1
- package/dist/src/coordinator/connection/types.d.ts +22 -1
- package/dist/src/devices/DeviceManager.d.ts +1 -0
- package/dist/src/types.d.ts +5 -37
- package/package.json +1 -1
- package/src/Call.ts +40 -85
- package/src/coordinator/connection/types.ts +23 -0
- package/src/devices/DeviceManager.ts +20 -1
- package/src/devices/SpeakerManager.ts +0 -1
- package/src/devices/__tests__/DeviceManager.test.ts +8 -0
- package/src/devices/__tests__/mocks.ts +4 -0
- package/src/events/call.ts +0 -3
- package/src/helpers/AudioBindingsWatchdog.ts +14 -2
- package/src/helpers/__tests__/AudioBindingsWatchdog.test.ts +27 -1
- package/src/store/stateStore.ts +1 -1
- package/src/types.ts +5 -48
package/dist/index.es.js
CHANGED
|
@@ -4785,7 +4785,7 @@ class StreamVideoWriteableStateStore {
|
|
|
4785
4785
|
* The currently connected user.
|
|
4786
4786
|
*/
|
|
4787
4787
|
get connectedUser() {
|
|
4788
|
-
return this.connectedUserSubject
|
|
4788
|
+
return getCurrentValue(this.connectedUserSubject);
|
|
4789
4789
|
}
|
|
4790
4790
|
/**
|
|
4791
4791
|
* A list of {@link Call} objects created/tracked by this client.
|
|
@@ -6284,7 +6284,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6284
6284
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6285
6285
|
};
|
|
6286
6286
|
|
|
6287
|
-
const version = "1.
|
|
6287
|
+
const version = "1.45.0";
|
|
6288
6288
|
const [major, minor, patch] = version.split('.');
|
|
6289
6289
|
let sdkInfo = {
|
|
6290
6290
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -8983,7 +8983,6 @@ const watchCallRejected = (call) => {
|
|
|
8983
8983
|
else {
|
|
8984
8984
|
if (rejectedBy[eventCall.created_by.id]) {
|
|
8985
8985
|
call.logger.info('call creator rejected, leaving call');
|
|
8986
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(call, 'remote');
|
|
8987
8986
|
await call.leave({ message: 'ring: creator rejected' });
|
|
8988
8987
|
}
|
|
8989
8988
|
}
|
|
@@ -8994,7 +8993,6 @@ const watchCallRejected = (call) => {
|
|
|
8994
8993
|
*/
|
|
8995
8994
|
const watchCallEnded = (call) => {
|
|
8996
8995
|
return function onCallEnded() {
|
|
8997
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(call, 'remote');
|
|
8998
8996
|
const { callingState } = call.state;
|
|
8999
8997
|
if (callingState !== CallingState.IDLE &&
|
|
9000
8998
|
callingState !== CallingState.LEFT) {
|
|
@@ -9026,7 +9024,6 @@ const watchSfuCallEnded = (call) => {
|
|
|
9026
9024
|
// update the call state to reflect the call has ended.
|
|
9027
9025
|
call.state.setEndedAt(new Date());
|
|
9028
9026
|
const reason = CallEndedReason[e.reason];
|
|
9029
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(call, 'remote');
|
|
9030
9027
|
await call.leave({ message: `callEnded received: ${reason}` });
|
|
9031
9028
|
}
|
|
9032
9029
|
catch (err) {
|
|
@@ -9553,11 +9550,14 @@ class AudioBindingsWatchdog {
|
|
|
9553
9550
|
for (const p of this.state.participants) {
|
|
9554
9551
|
if (p.isLocalParticipant)
|
|
9555
9552
|
continue;
|
|
9556
|
-
const { audioStream, screenShareAudioStream, sessionId, userId } = p;
|
|
9557
|
-
if (audioStream &&
|
|
9553
|
+
const { audioStream, screenShareAudioStream, sessionId, userId, publishedTracks, } = p;
|
|
9554
|
+
if (audioStream &&
|
|
9555
|
+
publishedTracks.includes(TrackType.AUDIO) &&
|
|
9556
|
+
!this.bindings.has(toBindingKey(sessionId))) {
|
|
9558
9557
|
danglingUserIds.push(userId);
|
|
9559
9558
|
}
|
|
9560
9559
|
if (screenShareAudioStream &&
|
|
9560
|
+
publishedTracks.includes(TrackType.SCREEN_SHARE_AUDIO) &&
|
|
9561
9561
|
!this.bindings.has(toBindingKey(sessionId, 'screenShareAudioTrack'))) {
|
|
9562
9562
|
danglingUserIds.push(userId);
|
|
9563
9563
|
}
|
|
@@ -11191,6 +11191,7 @@ class DeviceManager {
|
|
|
11191
11191
|
isDeviceReplaced = true;
|
|
11192
11192
|
}
|
|
11193
11193
|
if (isDeviceDisconnected) {
|
|
11194
|
+
this.dispatchDeviceDisconnectedEvent(prevDevice);
|
|
11194
11195
|
await this.disable();
|
|
11195
11196
|
await this.select(undefined);
|
|
11196
11197
|
}
|
|
@@ -11200,7 +11201,7 @@ class DeviceManager {
|
|
|
11200
11201
|
await this.enable();
|
|
11201
11202
|
this.isTrackStoppedDueToTrackEnd = false;
|
|
11202
11203
|
}
|
|
11203
|
-
else {
|
|
11204
|
+
else if (!hasPending(this.statusChangeConcurrencyTag)) {
|
|
11204
11205
|
await this.applySettingsToStream();
|
|
11205
11206
|
}
|
|
11206
11207
|
}
|
|
@@ -11214,6 +11215,20 @@ class DeviceManager {
|
|
|
11214
11215
|
const kind = this.mediaDeviceKind;
|
|
11215
11216
|
return devices.find((d) => d.deviceId === deviceId && d.kind === kind);
|
|
11216
11217
|
}
|
|
11218
|
+
dispatchDeviceDisconnectedEvent(device) {
|
|
11219
|
+
const event = {
|
|
11220
|
+
type: 'device.disconnected',
|
|
11221
|
+
call_cid: this.call.cid,
|
|
11222
|
+
status: this.isTrackStoppedDueToTrackEnd
|
|
11223
|
+
? this.state.prevStatus
|
|
11224
|
+
: this.state.status,
|
|
11225
|
+
deviceId: device.deviceId,
|
|
11226
|
+
label: device.label,
|
|
11227
|
+
kind: device.kind,
|
|
11228
|
+
};
|
|
11229
|
+
this.call.tracer.trace('device.disconnected', event);
|
|
11230
|
+
this.call.streamClient.dispatchEvent(event);
|
|
11231
|
+
}
|
|
11217
11232
|
persistPreference(selectedDevice, status) {
|
|
11218
11233
|
const deviceKind = this.mediaDeviceKind;
|
|
11219
11234
|
const deviceKey = deviceKind === 'audioinput' ? 'microphone' : 'camera';
|
|
@@ -12630,7 +12645,6 @@ class SpeakerManager {
|
|
|
12630
12645
|
this.defaultDevice = defaultDevice;
|
|
12631
12646
|
globalThis.streamRNVideoSDK?.callManager.setup({
|
|
12632
12647
|
defaultDevice,
|
|
12633
|
-
isRingingTypeCall: this.call.ringing,
|
|
12634
12648
|
});
|
|
12635
12649
|
}
|
|
12636
12650
|
}
|
|
@@ -12830,7 +12844,6 @@ class Call {
|
|
|
12830
12844
|
const currentUserId = this.currentUserId;
|
|
12831
12845
|
if (currentUserId && blockedUserIds.includes(currentUserId)) {
|
|
12832
12846
|
this.logger.info('Leaving call because of being blocked');
|
|
12833
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(this, 'restricted');
|
|
12834
12847
|
await this.leave({ message: 'user blocked' }).catch((err) => {
|
|
12835
12848
|
this.logger.error('Error leaving call after being blocked', err);
|
|
12836
12849
|
});
|
|
@@ -12867,7 +12880,6 @@ class Call {
|
|
|
12867
12880
|
const isAcceptedElsewhere = isAcceptedByMe && this.state.callingState === CallingState.RINGING;
|
|
12868
12881
|
if ((isAcceptedElsewhere || isRejectedByMe) &&
|
|
12869
12882
|
!hasPending(this.joinLeaveConcurrencyTag)) {
|
|
12870
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(this, isAcceptedElsewhere ? 'answeredElsewhere' : 'rejected');
|
|
12871
12883
|
this.leave().catch(() => {
|
|
12872
12884
|
this.logger.error('Could not leave a call that was accepted or rejected elsewhere');
|
|
12873
12885
|
});
|
|
@@ -12879,9 +12891,6 @@ class Call {
|
|
|
12879
12891
|
const receiver_id = this.clientStore.connectedUser?.id;
|
|
12880
12892
|
const ended_at = callSession?.ended_at;
|
|
12881
12893
|
const created_by_id = this.state.createdBy?.id;
|
|
12882
|
-
if (this.currentUserId && created_by_id === this.currentUserId) {
|
|
12883
|
-
globalThis.streamRNVideoSDK?.callingX?.registerOutgoingCall(this);
|
|
12884
|
-
}
|
|
12885
12894
|
const rejected_by = callSession?.rejected_by;
|
|
12886
12895
|
const accepted_by = callSession?.accepted_by;
|
|
12887
12896
|
let leaveCallIdle = false;
|
|
@@ -13020,28 +13029,17 @@ class Call {
|
|
|
13020
13029
|
}
|
|
13021
13030
|
if (callingState === CallingState.RINGING && reject !== false) {
|
|
13022
13031
|
if (reject) {
|
|
13023
|
-
|
|
13024
|
-
timeout: 'missed',
|
|
13025
|
-
cancel: 'canceled',
|
|
13026
|
-
busy: 'busy',
|
|
13027
|
-
decline: 'rejected',
|
|
13028
|
-
};
|
|
13029
|
-
const rejectReason = reason ?? 'decline';
|
|
13030
|
-
const endCallReason = reasonToEndCallReason[rejectReason] ?? 'rejected';
|
|
13031
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(this, endCallReason);
|
|
13032
|
-
await this.reject(rejectReason);
|
|
13032
|
+
await this.reject(reason ?? 'decline');
|
|
13033
13033
|
}
|
|
13034
13034
|
else {
|
|
13035
13035
|
// if reject was undefined, we still have to cancel the call automatically
|
|
13036
13036
|
// when I am the creator and everyone else left the call
|
|
13037
13037
|
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
|
|
13038
13038
|
if (this.isCreatedByMe && !hasOtherParticipants) {
|
|
13039
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(this, 'canceled');
|
|
13040
13039
|
await this.reject('cancel');
|
|
13041
13040
|
}
|
|
13042
13041
|
}
|
|
13043
13042
|
}
|
|
13044
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(this);
|
|
13045
13043
|
this.statsReporter?.stop();
|
|
13046
13044
|
this.statsReporter = undefined;
|
|
13047
13045
|
const leaveReason = message ?? reason ?? 'user is leaving the call';
|
|
@@ -13068,9 +13066,7 @@ class Call {
|
|
|
13068
13066
|
this.ringingSubject.next(false);
|
|
13069
13067
|
this.cancelAutoDrop();
|
|
13070
13068
|
this.clientStore.unregisterCall(this);
|
|
13071
|
-
globalThis.streamRNVideoSDK?.callManager.stop(
|
|
13072
|
-
isRingingTypeCall: this.ringing,
|
|
13073
|
-
});
|
|
13069
|
+
globalThis.streamRNVideoSDK?.callManager.stop();
|
|
13074
13070
|
this.camera.dispose();
|
|
13075
13071
|
this.microphone.dispose();
|
|
13076
13072
|
this.screenShare.dispose();
|
|
@@ -13236,19 +13232,11 @@ class Call {
|
|
|
13236
13232
|
* @returns a promise which resolves once the call join-flow has finished.
|
|
13237
13233
|
*/
|
|
13238
13234
|
this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, ...data } = {}) => {
|
|
13235
|
+
await this.setup();
|
|
13239
13236
|
const callingState = this.state.callingState;
|
|
13240
13237
|
if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
|
|
13241
13238
|
throw new Error(`Illegal State: call.join() shall be called only once`);
|
|
13242
13239
|
}
|
|
13243
|
-
if (data?.ring) {
|
|
13244
|
-
this.ringingSubject.next(true);
|
|
13245
|
-
}
|
|
13246
|
-
const callingX = globalThis.streamRNVideoSDK?.callingX;
|
|
13247
|
-
if (callingX) {
|
|
13248
|
-
// for Android/iOS, we need to start the call in the callingx library as soon as possible
|
|
13249
|
-
await callingX.joinCall(this, this.clientStore.calls);
|
|
13250
|
-
}
|
|
13251
|
-
await this.setup();
|
|
13252
13240
|
this.joinResponseTimeout = joinResponseTimeout;
|
|
13253
13241
|
this.rpcRequestTimeout = rpcRequestTimeout;
|
|
13254
13242
|
// we will count the number of join failures per SFU.
|
|
@@ -13257,44 +13245,38 @@ class Call {
|
|
|
13257
13245
|
const sfuJoinFailures = new Map();
|
|
13258
13246
|
const joinData = data;
|
|
13259
13247
|
maxJoinRetries = Math.max(maxJoinRetries, 1);
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
13264
|
-
|
|
13265
|
-
|
|
13266
|
-
|
|
13267
|
-
|
|
13248
|
+
for (let attempt = 0; attempt < maxJoinRetries; attempt++) {
|
|
13249
|
+
try {
|
|
13250
|
+
this.logger.trace(`Joining call (${attempt})`, this.cid);
|
|
13251
|
+
await this.doJoin(data);
|
|
13252
|
+
delete joinData.migrating_from;
|
|
13253
|
+
delete joinData.migrating_from_list;
|
|
13254
|
+
break;
|
|
13255
|
+
}
|
|
13256
|
+
catch (err) {
|
|
13257
|
+
this.logger.warn(`Failed to join call (${attempt})`, this.cid);
|
|
13258
|
+
if ((err instanceof ErrorFromResponse && err.unrecoverable) ||
|
|
13259
|
+
(err instanceof SfuJoinError && err.unrecoverable)) {
|
|
13260
|
+
// if the error is unrecoverable, we should not retry as that signals
|
|
13261
|
+
// that connectivity is good, but the coordinator doesn't allow the user
|
|
13262
|
+
// to join the call due to some reason (e.g., ended call, expired token...)
|
|
13263
|
+
throw err;
|
|
13268
13264
|
}
|
|
13269
|
-
|
|
13270
|
-
|
|
13271
|
-
|
|
13272
|
-
|
|
13273
|
-
|
|
13274
|
-
|
|
13275
|
-
|
|
13276
|
-
|
|
13277
|
-
|
|
13278
|
-
|
|
13279
|
-
|
|
13280
|
-
|
|
13281
|
-
const sfuId = this.credentials?.server.edge_name || '';
|
|
13282
|
-
const failures = (sfuJoinFailures.get(sfuId) || 0) + 1;
|
|
13283
|
-
sfuJoinFailures.set(sfuId, failures);
|
|
13284
|
-
if (switchSfu || failures >= 2) {
|
|
13285
|
-
joinData.migrating_from = sfuId;
|
|
13286
|
-
joinData.migrating_from_list = Array.from(sfuJoinFailures.keys());
|
|
13287
|
-
}
|
|
13288
|
-
if (attempt === maxJoinRetries - 1) {
|
|
13289
|
-
throw err;
|
|
13290
|
-
}
|
|
13265
|
+
// immediately switch to a different SFU in case of recoverable join error
|
|
13266
|
+
const switchSfu = err instanceof SfuJoinError &&
|
|
13267
|
+
SfuJoinError.isJoinErrorCode(err.errorEvent);
|
|
13268
|
+
const sfuId = this.credentials?.server.edge_name || '';
|
|
13269
|
+
const failures = (sfuJoinFailures.get(sfuId) || 0) + 1;
|
|
13270
|
+
sfuJoinFailures.set(sfuId, failures);
|
|
13271
|
+
if (switchSfu || failures >= 2) {
|
|
13272
|
+
joinData.migrating_from = sfuId;
|
|
13273
|
+
joinData.migrating_from_list = Array.from(sfuJoinFailures.keys());
|
|
13274
|
+
}
|
|
13275
|
+
if (attempt === maxJoinRetries - 1) {
|
|
13276
|
+
throw err;
|
|
13291
13277
|
}
|
|
13292
|
-
await sleep(retryInterval(attempt));
|
|
13293
13278
|
}
|
|
13294
|
-
|
|
13295
|
-
catch (error) {
|
|
13296
|
-
callingX?.endCall(this, 'error');
|
|
13297
|
-
throw error;
|
|
13279
|
+
await sleep(retryInterval(attempt));
|
|
13298
13280
|
}
|
|
13299
13281
|
};
|
|
13300
13282
|
/**
|
|
@@ -13441,9 +13423,7 @@ class Call {
|
|
|
13441
13423
|
// re-apply them on later reconnections or server-side data fetches
|
|
13442
13424
|
if (!this.deviceSettingsAppliedOnce && this.state.settings) {
|
|
13443
13425
|
await this.applyDeviceConfig(this.state.settings, true, false);
|
|
13444
|
-
globalThis.streamRNVideoSDK?.callManager.start(
|
|
13445
|
-
isRingingTypeCall: this.ringing,
|
|
13446
|
-
});
|
|
13426
|
+
globalThis.streamRNVideoSDK?.callManager.start();
|
|
13447
13427
|
this.deviceSettingsAppliedOnce = true;
|
|
13448
13428
|
}
|
|
13449
13429
|
// We shouldn't persist the `ring` and `notify` state after joining the call
|
|
@@ -13871,7 +13851,6 @@ class Call {
|
|
|
13871
13851
|
if (strategy === WebsocketReconnectStrategy.UNSPECIFIED)
|
|
13872
13852
|
return;
|
|
13873
13853
|
if (strategy === WebsocketReconnectStrategy.DISCONNECT) {
|
|
13874
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(this, 'error');
|
|
13875
13854
|
this.leave({ message: 'SFU instructed to disconnect' }).catch((err) => {
|
|
13876
13855
|
this.logger.warn(`Can't leave call after disconnect request`, err);
|
|
13877
13856
|
});
|
|
@@ -14893,7 +14872,7 @@ class Call {
|
|
|
14893
14872
|
* A flag indicating whether the call was created by the current user.
|
|
14894
14873
|
*/
|
|
14895
14874
|
get isCreatedByMe() {
|
|
14896
|
-
return
|
|
14875
|
+
return this.state.createdBy?.id === this.currentUserId;
|
|
14897
14876
|
}
|
|
14898
14877
|
}
|
|
14899
14878
|
|
|
@@ -16017,7 +15996,7 @@ class StreamClient {
|
|
|
16017
15996
|
this.getUserAgent = () => {
|
|
16018
15997
|
if (!this.cachedUserAgent) {
|
|
16019
15998
|
const { clientAppIdentifier = {} } = this.options;
|
|
16020
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
15999
|
+
const { sdkName = 'js', sdkVersion = "1.45.0", ...extras } = clientAppIdentifier;
|
|
16021
16000
|
this.cachedUserAgent = [
|
|
16022
16001
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
16023
16002
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|