@stream-io/video-client 1.12.2 → 1.12.4
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 +18 -0
- package/dist/index.browser.es.js +73 -45
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +73 -45
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +73 -45
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +4 -0
- package/package.json +4 -4
- package/src/Call.ts +40 -30
- package/src/devices/BrowserPermission.ts +7 -3
- package/src/devices/devices.ts +21 -18
- package/src/helpers/DynascaleManager.ts +19 -5
package/dist/src/Call.d.ts
CHANGED
|
@@ -540,6 +540,10 @@ export declare class Call {
|
|
|
540
540
|
* Applicable only for ringing calls.
|
|
541
541
|
*/
|
|
542
542
|
private scheduleAutoDrop;
|
|
543
|
+
/**
|
|
544
|
+
* Cancels a scheduled auto-drop timeout.
|
|
545
|
+
*/
|
|
546
|
+
private cancelAutoDrop;
|
|
543
547
|
/**
|
|
544
548
|
* Retrieves the list of recordings for the current call or call session.
|
|
545
549
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-client",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.4",
|
|
4
4
|
"packageManager": "yarn@3.2.4",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"start": "rollup -w -c",
|
|
14
14
|
"build": "yarn clean && rollup -c",
|
|
15
15
|
"test": "vitest",
|
|
16
|
-
"test-ci": "vitest --coverage",
|
|
16
|
+
"test-ci": "vitest run --coverage",
|
|
17
17
|
"generate:open-api": "./generate-openapi.sh protocol",
|
|
18
18
|
"generate:open-api:dev": "./generate-openapi.sh chat",
|
|
19
19
|
"generate:timer-worker": "./generate-timer-worker.sh"
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@stream-io/node-sdk": "^0.4.3",
|
|
46
46
|
"@types/sdp-transform": "^2.4.7",
|
|
47
47
|
"@types/ua-parser-js": "^0.7.37",
|
|
48
|
-
"@vitest/coverage-v8": "^2.1.
|
|
48
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
49
49
|
"dotenv": "^16.3.1",
|
|
50
50
|
"happy-dom": "^11.0.2",
|
|
51
51
|
"prettier": "^3.3.2",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"rollup": "^4.22.0",
|
|
54
54
|
"typescript": "^5.5.2",
|
|
55
55
|
"vite": "^5.4.6",
|
|
56
|
-
"vitest": "^2.1.
|
|
56
|
+
"vitest": "^2.1.8",
|
|
57
57
|
"vitest-mock-extended": "^2.0.2"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/Call.ts
CHANGED
|
@@ -342,16 +342,18 @@ export class Call {
|
|
|
342
342
|
);
|
|
343
343
|
|
|
344
344
|
this.leaveCallHooks.add(
|
|
345
|
-
//
|
|
346
|
-
createSubscription(this.state.
|
|
345
|
+
// cancel auto-drop when call is
|
|
346
|
+
createSubscription(this.state.session$, (session) => {
|
|
347
347
|
if (!this.ringing) return;
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
|
|
348
|
+
|
|
349
|
+
const receiverId = this.clientStore.connectedUser?.id;
|
|
350
|
+
if (!receiverId) return;
|
|
351
|
+
|
|
352
|
+
const isAcceptedByMe = Boolean(session?.accepted_by[receiverId]);
|
|
353
|
+
const isRejectedByMe = Boolean(session?.rejected_by[receiverId]);
|
|
354
|
+
|
|
355
|
+
if (isAcceptedByMe || isRejectedByMe) {
|
|
356
|
+
this.cancelAutoDrop();
|
|
355
357
|
}
|
|
356
358
|
}),
|
|
357
359
|
);
|
|
@@ -2004,28 +2006,36 @@ export class Call {
|
|
|
2004
2006
|
* Applicable only for ringing calls.
|
|
2005
2007
|
*/
|
|
2006
2008
|
private scheduleAutoDrop = () => {
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2009
|
+
this.cancelAutoDrop();
|
|
2010
|
+
|
|
2011
|
+
const settings = this.state.settings;
|
|
2012
|
+
if (!settings) return;
|
|
2013
|
+
// ignore if the call is not ringing
|
|
2014
|
+
if (this.state.callingState !== CallingState.RINGING) return;
|
|
2015
|
+
|
|
2016
|
+
const timeoutInMs = this.isCreatedByMe
|
|
2017
|
+
? settings.ring.auto_cancel_timeout_ms
|
|
2018
|
+
: settings.ring.incoming_call_timeout_ms;
|
|
2019
|
+
|
|
2020
|
+
// 0 means no auto-drop
|
|
2021
|
+
if (timeoutInMs <= 0) return;
|
|
2022
|
+
|
|
2023
|
+
this.dropTimeout = setTimeout(() => {
|
|
2024
|
+
// the call might have stopped ringing by this point,
|
|
2025
|
+
// e.g. it was already accepted and joined
|
|
2026
|
+
if (this.state.callingState !== CallingState.RINGING) return;
|
|
2027
|
+
this.leave({ reject: true, reason: 'timeout' }).catch((err) => {
|
|
2028
|
+
this.logger('error', 'Failed to drop call', err);
|
|
2029
|
+
});
|
|
2030
|
+
}, timeoutInMs);
|
|
2031
|
+
};
|
|
2020
2032
|
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
}),
|
|
2028
|
-
);
|
|
2033
|
+
/**
|
|
2034
|
+
* Cancels a scheduled auto-drop timeout.
|
|
2035
|
+
*/
|
|
2036
|
+
private cancelAutoDrop = () => {
|
|
2037
|
+
clearTimeout(this.dropTimeout);
|
|
2038
|
+
this.dropTimeout = undefined;
|
|
2029
2039
|
};
|
|
2030
2040
|
|
|
2031
2041
|
/**
|
|
@@ -73,9 +73,8 @@ export class BrowserPermission {
|
|
|
73
73
|
const isGranted = this.state === 'granted';
|
|
74
74
|
|
|
75
75
|
if (!isGranted && throwOnNotAllowed) {
|
|
76
|
-
throw new
|
|
76
|
+
throw new Error(
|
|
77
77
|
'Permission was not granted previously, and prompting again is not allowed',
|
|
78
|
-
'NotAllowedError',
|
|
79
78
|
);
|
|
80
79
|
}
|
|
81
80
|
|
|
@@ -91,7 +90,12 @@ export class BrowserPermission {
|
|
|
91
90
|
this.setState('granted');
|
|
92
91
|
return true;
|
|
93
92
|
} catch (e) {
|
|
94
|
-
if (
|
|
93
|
+
if (
|
|
94
|
+
e &&
|
|
95
|
+
typeof e === 'object' &&
|
|
96
|
+
'name' in e &&
|
|
97
|
+
(e.name === 'NotAllowedError' || e.name === 'SecurityError')
|
|
98
|
+
) {
|
|
95
99
|
this.logger('info', 'Browser permission was not granted', {
|
|
96
100
|
permission: this.permission,
|
|
97
101
|
});
|
package/src/devices/devices.ts
CHANGED
|
@@ -169,6 +169,17 @@ const getStream = async (constraints: MediaStreamConstraints) => {
|
|
|
169
169
|
return stream;
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
+
function isOverconstrainedError(error: unknown) {
|
|
173
|
+
return (
|
|
174
|
+
error &&
|
|
175
|
+
typeof error === 'object' &&
|
|
176
|
+
(('name' in error && error.name === 'OverconstrainedError') ||
|
|
177
|
+
('message' in error &&
|
|
178
|
+
typeof error.message === 'string' &&
|
|
179
|
+
error.message.startsWith('OverconstrainedError')))
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
172
183
|
/**
|
|
173
184
|
* Returns an audio media stream that fulfills the given constraints.
|
|
174
185
|
* If no constraints are provided, it uses the browser's default ones.
|
|
@@ -194,18 +205,14 @@ export const getAudioStream = async (
|
|
|
194
205
|
});
|
|
195
206
|
return await getStream(constraints);
|
|
196
207
|
} catch (error) {
|
|
197
|
-
if (
|
|
198
|
-
|
|
199
|
-
error.name === 'OverconstrainedError' &&
|
|
200
|
-
trackConstraints?.deviceId
|
|
201
|
-
) {
|
|
202
|
-
const { deviceId, ...relaxedContraints } = trackConstraints;
|
|
208
|
+
if (isOverconstrainedError(error) && trackConstraints?.deviceId) {
|
|
209
|
+
const { deviceId, ...relaxedConstraints } = trackConstraints;
|
|
203
210
|
getLogger(['devices'])(
|
|
204
211
|
'warn',
|
|
205
|
-
'Failed to get audio stream, will try again with relaxed
|
|
206
|
-
{ error, constraints,
|
|
212
|
+
'Failed to get audio stream, will try again with relaxed constraints',
|
|
213
|
+
{ error, constraints, relaxedConstraints },
|
|
207
214
|
);
|
|
208
|
-
return getAudioStream(
|
|
215
|
+
return getAudioStream(relaxedConstraints);
|
|
209
216
|
}
|
|
210
217
|
|
|
211
218
|
getLogger(['devices'])('error', 'Failed to get audio stream', {
|
|
@@ -240,18 +247,14 @@ export const getVideoStream = async (
|
|
|
240
247
|
});
|
|
241
248
|
return await getStream(constraints);
|
|
242
249
|
} catch (error) {
|
|
243
|
-
if (
|
|
244
|
-
|
|
245
|
-
error.name === 'OverconstrainedError' &&
|
|
246
|
-
trackConstraints?.deviceId
|
|
247
|
-
) {
|
|
248
|
-
const { deviceId, ...relaxedContraints } = trackConstraints;
|
|
250
|
+
if (isOverconstrainedError(error) && trackConstraints?.deviceId) {
|
|
251
|
+
const { deviceId, ...relaxedConstraints } = trackConstraints;
|
|
249
252
|
getLogger(['devices'])(
|
|
250
253
|
'warn',
|
|
251
|
-
'Failed to get video stream, will try again with relaxed
|
|
252
|
-
{ error, constraints,
|
|
254
|
+
'Failed to get video stream, will try again with relaxed constraints',
|
|
255
|
+
{ error, constraints, relaxedConstraints },
|
|
253
256
|
);
|
|
254
|
-
return getVideoStream(
|
|
257
|
+
return getVideoStream(relaxedConstraints);
|
|
255
258
|
}
|
|
256
259
|
|
|
257
260
|
getLogger(['devices'])('error', 'Failed to get video stream', {
|
|
@@ -371,11 +371,14 @@ export class DynascaleManager {
|
|
|
371
371
|
});
|
|
372
372
|
});
|
|
373
373
|
|
|
374
|
-
let lastDimensions:
|
|
374
|
+
let lastDimensions: VideoDimension | undefined;
|
|
375
375
|
const resizeObserver = boundParticipant.isLocalParticipant
|
|
376
376
|
? null
|
|
377
377
|
: new ResizeObserver(() => {
|
|
378
|
-
const currentDimensions =
|
|
378
|
+
const currentDimensions = {
|
|
379
|
+
width: videoElement.clientWidth,
|
|
380
|
+
height: videoElement.clientHeight,
|
|
381
|
+
};
|
|
379
382
|
|
|
380
383
|
// skip initial trigger
|
|
381
384
|
if (!lastDimensions) {
|
|
@@ -384,13 +387,24 @@ export class DynascaleManager {
|
|
|
384
387
|
}
|
|
385
388
|
|
|
386
389
|
if (
|
|
387
|
-
lastDimensions === currentDimensions
|
|
390
|
+
(lastDimensions.width === currentDimensions.width &&
|
|
391
|
+
lastDimensions.height === currentDimensions.height) ||
|
|
388
392
|
viewportVisibilityState === VisibilityState.INVISIBLE
|
|
389
393
|
) {
|
|
390
394
|
return;
|
|
391
395
|
}
|
|
392
396
|
|
|
393
|
-
|
|
397
|
+
const relativeDelta = Math.max(
|
|
398
|
+
currentDimensions.width / lastDimensions.width,
|
|
399
|
+
currentDimensions.height / lastDimensions.height,
|
|
400
|
+
);
|
|
401
|
+
// Low quality video in an upscaled video element is very noticable.
|
|
402
|
+
// We try to upscale faster, and downscale slower. We also update debounce
|
|
403
|
+
// more if the size change is not significant, gurading against fast-firing
|
|
404
|
+
// resize events.
|
|
405
|
+
const debounceType =
|
|
406
|
+
relativeDelta > 1.2 ? DebounceType.IMMEDIATE : DebounceType.MEDIUM;
|
|
407
|
+
requestTrackWithDimensions(debounceType, {
|
|
394
408
|
width: videoElement.clientWidth,
|
|
395
409
|
height: videoElement.clientHeight,
|
|
396
410
|
});
|
|
@@ -413,7 +427,7 @@ export class DynascaleManager {
|
|
|
413
427
|
.subscribe((isPublishing) => {
|
|
414
428
|
if (isPublishing) {
|
|
415
429
|
// the participant just started to publish a track
|
|
416
|
-
requestTrackWithDimensions(DebounceType.
|
|
430
|
+
requestTrackWithDimensions(DebounceType.IMMEDIATE, {
|
|
417
431
|
width: videoElement.clientWidth,
|
|
418
432
|
height: videoElement.clientHeight,
|
|
419
433
|
});
|