Skip to content

Commit 7d2867b

Browse files
committed
Merge branch 'main' into improve-ws-error-logging
2 parents f47357d + 3c81092 commit 7d2867b

File tree

280 files changed

+12034
-5663
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

280 files changed

+12034
-5663
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"license": "See license in LICENSE",
88
"workspaces": [
99
"packages/*",
10-
"sample-apps/**/*"
10+
"sample-apps/*/*"
1111
],
1212
"scripts": {
1313
"start:react:sdk": "yarn workspace @stream-io/video-react-sdk run start",

packages/client/CHANGELOG.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,55 @@
22

33
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
44

5+
## [1.11.6](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.5...@stream-io/video-client-1.11.6) (2024-11-22)
6+
7+
8+
### Bug Fixes
9+
10+
* force single codec preference in the SDP ([#1588](https://github.com/GetStream/stream-video-js/issues/1588)) ([4afff09](https://github.com/GetStream/stream-video-js/commit/4afff09a778f8567176d22bcc22d36001dca7cd3)), closes [#1581](https://github.com/GetStream/stream-video-js/issues/1581)
11+
12+
## [1.11.5](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.4...@stream-io/video-client-1.11.5) (2024-11-22)
13+
14+
15+
### Bug Fixes
16+
17+
* unhandled promise rejections during reconnect ([#1585](https://github.com/GetStream/stream-video-js/issues/1585)) ([920c4ea](https://github.com/GetStream/stream-video-js/commit/920c4ea3b3f622430b35ac1bade74a6206ee17e5)), closes [/github.com/GetStream/stream-video-js/pull/1585/files#diff-420f6ddab47c1be72fd9ce8c99e1fa2b9f5f0495b7c367546ee0ff634beaed81](https://github.com/GetStream//github.com/GetStream/stream-video-js/pull/1585/files/issues/diff-420f6ddab47c1be72fd9ce8c99e1fa2b9f5f0495b7c367546ee0ff634beaed81)
18+
19+
## [1.11.4](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.3...@stream-io/video-client-1.11.4) (2024-11-21)
20+
21+
22+
### Bug Fixes
23+
24+
* experimental option to force single codec preference in the SDP ([#1581](https://github.com/GetStream/stream-video-js/issues/1581)) ([894a86e](https://github.com/GetStream/stream-video-js/commit/894a86e407dc0dd36b7463bb964c86da0c3055d1))
25+
26+
## [1.11.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.2...@stream-io/video-client-1.11.3) (2024-11-20)
27+
28+
29+
### Bug Fixes
30+
31+
* respect codec overrides when computing the video layers ([#1582](https://github.com/GetStream/stream-video-js/issues/1582)) ([c22b83e](https://github.com/GetStream/stream-video-js/commit/c22b83ef710f2188e680b73790154de046a824e9))
32+
33+
## [1.11.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.1...@stream-io/video-client-1.11.2) (2024-11-14)
34+
35+
36+
### Bug Fixes
37+
38+
* fully reset token manager on user disconnect ([#1578](https://github.com/GetStream/stream-video-js/issues/1578)) ([6751abc](https://github.com/GetStream/stream-video-js/commit/6751abc0507085bd7c9f3f803f4c5929e0598bea)), closes [#1573](https://github.com/GetStream/stream-video-js/issues/1573)
39+
40+
## [1.11.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.0...@stream-io/video-client-1.11.1) (2024-11-14)
41+
42+
43+
### Bug Fixes
44+
45+
* reject was not called on timeout, decline and cancel scenarios ([#1576](https://github.com/GetStream/stream-video-js/issues/1576)) ([8be76a4](https://github.com/GetStream/stream-video-js/commit/8be76a447729aeba7f5c68f8a9bb85b4738cb76d))
46+
47+
## [1.11.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.10.5...@stream-io/video-client-1.11.0) (2024-11-13)
48+
49+
50+
### Features
51+
52+
* Connection timing ([#1574](https://github.com/GetStream/stream-video-js/issues/1574)) ([ce1dc9a](https://github.com/GetStream/stream-video-js/commit/ce1dc9a01fc5b0e60e3dac6653c27e99fd4b3ecb))
53+
554
## [1.10.5](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.10.4...@stream-io/video-client-1.10.5) (2024-11-07)
655

756

packages/client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@stream-io/video-client",
3-
"version": "1.10.5",
3+
"version": "1.11.6",
44
"packageManager": "[email protected]",
55
"main": "dist/index.cjs.js",
66
"module": "dist/index.es.js",

packages/client/src/Call.ts

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -516,20 +516,15 @@ export class Call {
516516
await waitUntilCallJoined();
517517
}
518518

519-
if (reject && this.ringing) {
520-
// I'm the one who started the call, so I should cancel it.
521-
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
522-
if (
523-
this.isCreatedByMe &&
524-
!hasOtherParticipants &&
525-
callingState === CallingState.RINGING
526-
) {
527-
// Signals other users that I have cancelled my call to them
528-
// before they accepted it.
529-
await this.reject();
530-
} else if (callingState === CallingState.RINGING) {
531-
// Signals other users that I have rejected the incoming call.
532-
await this.reject();
519+
if (callingState === CallingState.RINGING) {
520+
if (reject) {
521+
await this.reject(reason);
522+
} else {
523+
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
524+
if (this.isCreatedByMe && !hasOtherParticipants) {
525+
// I'm the one who started the call, so I should cancel it when there are no other participants.
526+
await this.reject('cancel');
527+
}
533528
}
534529
}
535530

@@ -725,6 +720,7 @@ export class Call {
725720
* @returns a promise which resolves once the call join-flow has finished.
726721
*/
727722
join = async (data?: JoinCallData): Promise<void> => {
723+
const connectStartTime = Date.now();
728724
await this.setup();
729725
const callingState = this.state.callingState;
730726
if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
@@ -793,11 +789,11 @@ export class Call {
793789
: undefined;
794790
const { callState, fastReconnectDeadlineSeconds } = await sfuClient.join({
795791
subscriberSdp: receivingCapabilitiesSdp,
792+
publisherSdp: '',
796793
clientDetails,
797794
fastReconnect: performingFastReconnect,
798795
reconnectDetails,
799796
});
800-
801797
this.fastReconnectDeadlineSeconds = fastReconnectDeadlineSeconds;
802798
if (callState) {
803799
this.state.updateFromSfuCallState(
@@ -831,6 +827,16 @@ export class Call {
831827
});
832828
}
833829

830+
// make sure we only track connection timing if we are not calling this method as part of a reconnection flow
831+
if (!performingRejoin && !performingFastReconnect && !performingMigration) {
832+
this.sfuStatsReporter?.sendTelemetryData({
833+
data: {
834+
oneofKind: 'connectionTimeSeconds',
835+
connectionTimeSeconds: (Date.now() - connectStartTime) / 1000,
836+
},
837+
});
838+
}
839+
834840
if (performingRejoin) {
835841
const strategy = WebsocketReconnectStrategy[this.reconnectStrategy];
836842
await previousSfuClient?.leaveAndClose(
@@ -1130,28 +1136,49 @@ export class Call {
11301136
* @internal
11311137
*/
11321138
private reconnectFast = async () => {
1139+
let reconnectStartTime = Date.now();
11331140
this.reconnectStrategy = WebsocketReconnectStrategy.FAST;
11341141
this.state.setCallingState(CallingState.RECONNECTING);
1135-
return this.join(this.joinCallData);
1142+
await this.join(this.joinCallData);
1143+
this.sfuStatsReporter?.sendTelemetryData({
1144+
data: {
1145+
oneofKind: 'reconnection',
1146+
reconnection: {
1147+
timeSeconds: (Date.now() - reconnectStartTime) / 1000,
1148+
strategy: WebsocketReconnectStrategy.FAST,
1149+
},
1150+
},
1151+
});
11361152
};
11371153

11381154
/**
11391155
* Initiates the reconnection flow with the "rejoin" strategy.
11401156
* @internal
11411157
*/
11421158
private reconnectRejoin = async () => {
1159+
let reconnectStartTime = Date.now();
11431160
this.reconnectStrategy = WebsocketReconnectStrategy.REJOIN;
11441161
this.state.setCallingState(CallingState.RECONNECTING);
11451162
await this.join(this.joinCallData);
11461163
await this.restorePublishedTracks();
11471164
this.restoreSubscribedTracks();
1165+
this.sfuStatsReporter?.sendTelemetryData({
1166+
data: {
1167+
oneofKind: 'reconnection',
1168+
reconnection: {
1169+
timeSeconds: (Date.now() - reconnectStartTime) / 1000,
1170+
strategy: WebsocketReconnectStrategy.REJOIN,
1171+
},
1172+
},
1173+
});
11481174
};
11491175

11501176
/**
11511177
* Initiates the reconnection flow with the "migrate" strategy.
11521178
* @internal
11531179
*/
11541180
private reconnectMigrate = async () => {
1181+
let reconnectStartTime = Date.now();
11551182
const currentSfuClient = this.sfuClient;
11561183
if (!currentSfuClient) {
11571184
throw new Error('Cannot migrate without an active SFU client');
@@ -1196,6 +1223,15 @@ export class Call {
11961223
// and close the previous SFU client, without specifying close code
11971224
currentSfuClient.close();
11981225
}
1226+
this.sfuStatsReporter?.sendTelemetryData({
1227+
data: {
1228+
oneofKind: 'reconnection',
1229+
reconnection: {
1230+
timeSeconds: (Date.now() - reconnectStartTime) / 1000,
1231+
strategy: WebsocketReconnectStrategy.MIGRATE,
1232+
},
1233+
},
1234+
});
11991235
};
12001236

12011237
/**
@@ -1919,13 +1955,16 @@ export class Call {
19191955
// ignore if the call is not ringing
19201956
if (this.state.callingState !== CallingState.RINGING) return;
19211957

1922-
const timeoutInMs = settings.ring.auto_cancel_timeout_ms;
1958+
const timeoutInMs = this.isCreatedByMe
1959+
? settings.ring.auto_cancel_timeout_ms
1960+
: settings.ring.incoming_call_timeout_ms;
1961+
19231962
// 0 means no auto-drop
19241963
if (timeoutInMs <= 0) return;
19251964

19261965
clearTimeout(this.dropTimeout);
19271966
this.dropTimeout = setTimeout(() => {
1928-
this.leave({ reason: 'ring: timeout' }).catch((err) => {
1967+
this.leave({ reject: true, reason: 'timeout' }).catch((err) => {
19291968
this.logger('error', 'Failed to drop call', err);
19301969
});
19311970
}, timeoutInMs);

packages/client/src/StreamVideoClient.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import { getLogger, logToConsole, setLogger } from './logger';
3131
import { getSdkInfo } from './client-details';
3232
import { SdkType } from './gen/video/sfu/models/models';
33+
import { withoutConcurrency } from './helpers/concurrency';
3334

3435
/**
3536
* A `StreamVideoClient` instance lets you communicate with our API, and authenticate users.
@@ -51,8 +52,9 @@ export class StreamVideoClient {
5152
streamClient: StreamClient;
5253

5354
protected eventHandlersToUnregister: Array<() => void> = [];
54-
protected connectionPromise: Promise<void | ConnectedEvent> | undefined;
55-
protected disconnectionPromise: Promise<void> | undefined;
55+
private readonly connectionConcurrencyTag = Symbol(
56+
'connectionConcurrencyTag',
57+
);
5658

5759
private static _instanceMap: Map<string, StreamVideoClient> = new Map();
5860

@@ -209,12 +211,11 @@ export class StreamVideoClient {
209211
return this.streamClient.connectGuestUser(user);
210212
};
211213
}
212-
this.connectionPromise = this.disconnectionPromise
213-
? this.disconnectionPromise.then(() => connectUser())
214-
: connectUser();
215214

216-
this.connectionPromise?.finally(() => (this.connectionPromise = undefined));
217-
const connectUserResponse = await this.connectionPromise;
215+
const connectUserResponse = await withoutConcurrency(
216+
this.connectionConcurrencyTag,
217+
() => connectUser(),
218+
);
218219
// connectUserResponse will be void if connectUser called twice for the same user
219220
if (connectUserResponse?.me) {
220221
this.writeableStateStore.setConnectedUser(connectUserResponse.me);
@@ -316,19 +317,15 @@ export class StreamVideoClient {
316317
* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
317318
*/
318319
disconnectUser = async (timeout?: number) => {
319-
if (!this.streamClient.user && !this.connectionPromise) {
320+
if (!this.streamClient.user) {
320321
return;
321322
}
322323
const userId = this.streamClient.user?.id;
323324
const apiKey = this.streamClient.key;
324325
const disconnectUser = () => this.streamClient.disconnectUser(timeout);
325-
this.disconnectionPromise = this.connectionPromise
326-
? this.connectionPromise.then(() => disconnectUser())
327-
: disconnectUser();
328-
this.disconnectionPromise.finally(
329-
() => (this.disconnectionPromise = undefined),
326+
await withoutConcurrency(this.connectionConcurrencyTag, () =>
327+
disconnectUser(),
330328
);
331-
await this.disconnectionPromise;
332329
if (userId) {
333330
StreamVideoClient._instanceMap.delete(apiKey + userId);
334331
}
@@ -556,10 +553,8 @@ export class StreamVideoClient {
556553
) => {
557554
const connectAnonymousUser = () =>
558555
this.streamClient.connectAnonymousUser(user, tokenOrProvider);
559-
this.connectionPromise = this.disconnectionPromise
560-
? this.disconnectionPromise.then(() => connectAnonymousUser())
561-
: connectAnonymousUser();
562-
this.connectionPromise.finally(() => (this.connectionPromise = undefined));
563-
return this.connectionPromise;
556+
return await withoutConcurrency(this.connectionConcurrencyTag, () =>
557+
connectAnonymousUser(),
558+
);
564559
};
565560
}

packages/client/src/__tests__/Call.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ describe('muting logic', () => {
272272
.mockImplementation(() => Promise.resolve({ duration: '0ms' }));
273273
});
274274

275-
it('should mute self', () => {
276-
call.muteSelf('audio');
275+
it('should mute self', async () => {
276+
await call.muteSelf('audio');
277277

278278
expect(spy).toHaveBeenCalledWith(userId, 'audio');
279279
});

packages/client/src/__tests__/StreamVideoClient.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ describe('StreamVideoClient', () => {
100100
expect(response.calls).toBeDefined();
101101
});
102102

103+
it('should clear token on disconnect', async () => {
104+
const user = { id: 'jane' };
105+
const tp = vi.fn(tokenProvider(user.id));
106+
await client.connectUser(user, tp);
107+
await client.disconnectUser();
108+
await client.connectUser({ type: 'anonymous' });
109+
expect(tp).toBeCalledTimes(1);
110+
});
111+
103112
afterEach(() => {
104113
client.disconnectUser();
105114
});

0 commit comments

Comments
 (0)