@stream-io/video-client 1.41.1 → 1.41.3
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 +11 -0
- package/dist/index.browser.es.js +55 -30
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +55 -30
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +55 -30
- package/dist/index.es.js.map +1 -1
- package/dist/src/stats/SfuStatsReporter.d.ts +2 -1
- package/dist/src/stats/types.d.ts +10 -0
- package/package.json +1 -1
- package/src/Call.ts +5 -9
- package/src/devices/SpeakerManager.ts +11 -15
- package/src/stats/CallStateStatsReporter.ts +22 -0
- package/src/stats/SfuStatsReporter.ts +22 -14
- package/src/stats/types.ts +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.41.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.41.2...@stream-io/video-client-1.41.3) (2026-01-30)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- **react:** improve logic for calculating the lower / upper threshold for video filter degradation ([#2094](https://github.com/GetStream/stream-video-js/issues/2094)) ([5cd2d5c](https://github.com/GetStream/stream-video-js/commit/5cd2d5cb34fc7bbdfaf9529eb9f8d33a40346cab))
|
|
10
|
+
- **stats:** adjust send stats frequency and include "leave reason" ([#2104](https://github.com/GetStream/stream-video-js/issues/2104)) ([0182832](https://github.com/GetStream/stream-video-js/commit/018283299bebe5d5078d4006ec86b6cd56884e77))
|
|
11
|
+
|
|
12
|
+
## [1.41.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.41.1...@stream-io/video-client-1.41.2) (2026-01-28)
|
|
13
|
+
|
|
14
|
+
- deduplicate RN compatibility assertions ([#2101](https://github.com/GetStream/stream-video-js/issues/2101)) ([5b9e6bc](https://github.com/GetStream/stream-video-js/commit/5b9e6bc227c55b067eea6345315bca015c8a7ee4))
|
|
15
|
+
|
|
5
16
|
## [1.41.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.41.0...@stream-io/video-client-1.41.1) (2026-01-26)
|
|
6
17
|
|
|
7
18
|
### Bug Fixes
|
package/dist/index.browser.es.js
CHANGED
|
@@ -6188,7 +6188,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6188
6188
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6189
6189
|
};
|
|
6190
6190
|
|
|
6191
|
-
const version = "1.41.
|
|
6191
|
+
const version = "1.41.3";
|
|
6192
6192
|
const [major, minor, patch] = version.split('.');
|
|
6193
6193
|
let sdkInfo = {
|
|
6194
6194
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -6511,6 +6511,16 @@ const createStatsReporter = ({ subscriber, publisher, state, datacenter, polling
|
|
|
6511
6511
|
stop,
|
|
6512
6512
|
};
|
|
6513
6513
|
};
|
|
6514
|
+
/**
|
|
6515
|
+
* Extracts camera statistics from a media source.
|
|
6516
|
+
*
|
|
6517
|
+
* @param mediaSource the media source stats to extract camera info from.
|
|
6518
|
+
*/
|
|
6519
|
+
const getCameraStats = (mediaSource) => ({
|
|
6520
|
+
frameRate: mediaSource.framesPerSecond,
|
|
6521
|
+
frameWidth: mediaSource.width,
|
|
6522
|
+
frameHeight: mediaSource.height,
|
|
6523
|
+
});
|
|
6514
6524
|
/**
|
|
6515
6525
|
* Transforms raw RTC stats into a slimmer and uniform across browsers format.
|
|
6516
6526
|
*
|
|
@@ -6536,6 +6546,7 @@ const transform = (report, opts) => {
|
|
|
6536
6546
|
}
|
|
6537
6547
|
let trackType;
|
|
6538
6548
|
let audioLevel;
|
|
6549
|
+
let camera;
|
|
6539
6550
|
let concealedSamples;
|
|
6540
6551
|
let concealmentEvents;
|
|
6541
6552
|
let packetsReceived;
|
|
@@ -6551,6 +6562,9 @@ const transform = (report, opts) => {
|
|
|
6551
6562
|
typeof mediaSource.audioLevel === 'number') {
|
|
6552
6563
|
audioLevel = mediaSource.audioLevel;
|
|
6553
6564
|
}
|
|
6565
|
+
if (trackKind === 'video') {
|
|
6566
|
+
camera = getCameraStats(mediaSource);
|
|
6567
|
+
}
|
|
6554
6568
|
}
|
|
6555
6569
|
}
|
|
6556
6570
|
else if (kind === 'subscriber' && trackKind === 'audio') {
|
|
@@ -6584,6 +6598,7 @@ const transform = (report, opts) => {
|
|
|
6584
6598
|
concealmentEvents,
|
|
6585
6599
|
packetsReceived,
|
|
6586
6600
|
packetsLost,
|
|
6601
|
+
camera,
|
|
6587
6602
|
};
|
|
6588
6603
|
});
|
|
6589
6604
|
return {
|
|
@@ -6603,6 +6618,7 @@ const getEmptyVideoStats = (stats) => {
|
|
|
6603
6618
|
highestFrameWidth: 0,
|
|
6604
6619
|
highestFrameHeight: 0,
|
|
6605
6620
|
highestFramesPerSecond: 0,
|
|
6621
|
+
camera: {},
|
|
6606
6622
|
codec: '',
|
|
6607
6623
|
codecPerTrackType: {},
|
|
6608
6624
|
timestamp: Date.now(),
|
|
@@ -6647,6 +6663,9 @@ const aggregate = (stats) => {
|
|
|
6647
6663
|
acc.highestFramesPerSecond = stream.framesPerSecond || 0;
|
|
6648
6664
|
maxArea = streamArea;
|
|
6649
6665
|
}
|
|
6666
|
+
if (stream.trackType === TrackType.VIDEO) {
|
|
6667
|
+
acc.camera = stream.camera;
|
|
6668
|
+
}
|
|
6650
6669
|
qualityLimitationReasons.add(stream.qualityLimitationReason || '');
|
|
6651
6670
|
return acc;
|
|
6652
6671
|
}, aggregatedStats);
|
|
@@ -6711,6 +6730,7 @@ const aggregateAudio = (stats) => {
|
|
|
6711
6730
|
class SfuStatsReporter {
|
|
6712
6731
|
constructor(sfuClient, { options, clientDetails, subscriber, publisher, microphone, camera, state, tracer, unifiedSessionId, }) {
|
|
6713
6732
|
this.logger = videoLoggerSystem.getLogger('SfuStatsReporter');
|
|
6733
|
+
this.reportCount = 0;
|
|
6714
6734
|
this.inputDevices = new Map();
|
|
6715
6735
|
this.observeDevice = (device, kind) => {
|
|
6716
6736
|
const { browserPermissionState$ } = device.state;
|
|
@@ -6812,17 +6832,31 @@ class SfuStatsReporter {
|
|
|
6812
6832
|
throw err;
|
|
6813
6833
|
}
|
|
6814
6834
|
};
|
|
6835
|
+
this.scheduleNextReport = () => {
|
|
6836
|
+
const intervals = [1500, 3000, 3000, 5000];
|
|
6837
|
+
if (this.reportCount < intervals.length) {
|
|
6838
|
+
this.timeoutId = setTimeout(() => {
|
|
6839
|
+
this.flush();
|
|
6840
|
+
this.reportCount++;
|
|
6841
|
+
this.scheduleNextReport();
|
|
6842
|
+
}, intervals[this.reportCount]);
|
|
6843
|
+
}
|
|
6844
|
+
else {
|
|
6845
|
+
clearInterval(this.intervalId);
|
|
6846
|
+
this.intervalId = setInterval(() => {
|
|
6847
|
+
this.flush();
|
|
6848
|
+
}, this.options.reporting_interval_ms);
|
|
6849
|
+
}
|
|
6850
|
+
};
|
|
6815
6851
|
this.start = () => {
|
|
6816
6852
|
if (this.options.reporting_interval_ms <= 0)
|
|
6817
6853
|
return;
|
|
6818
6854
|
this.observeDevice(this.microphone, 'mic');
|
|
6819
6855
|
this.observeDevice(this.camera, 'camera');
|
|
6856
|
+
this.reportCount = 0;
|
|
6820
6857
|
clearInterval(this.intervalId);
|
|
6821
|
-
this.
|
|
6822
|
-
|
|
6823
|
-
this.logger.warn('Failed to report stats', err);
|
|
6824
|
-
});
|
|
6825
|
-
}, this.options.reporting_interval_ms);
|
|
6858
|
+
clearTimeout(this.timeoutId);
|
|
6859
|
+
this.scheduleNextReport();
|
|
6826
6860
|
};
|
|
6827
6861
|
this.stop = () => {
|
|
6828
6862
|
this.unsubscribeDevicePermissionsSubscription?.();
|
|
@@ -6834,20 +6868,13 @@ class SfuStatsReporter {
|
|
|
6834
6868
|
this.intervalId = undefined;
|
|
6835
6869
|
clearTimeout(this.timeoutId);
|
|
6836
6870
|
this.timeoutId = undefined;
|
|
6871
|
+
this.reportCount = 0;
|
|
6837
6872
|
};
|
|
6838
6873
|
this.flush = () => {
|
|
6839
6874
|
this.run().catch((err) => {
|
|
6840
6875
|
this.logger.warn('Failed to flush report stats', err);
|
|
6841
6876
|
});
|
|
6842
6877
|
};
|
|
6843
|
-
this.scheduleOne = (timeout) => {
|
|
6844
|
-
clearTimeout(this.timeoutId);
|
|
6845
|
-
this.timeoutId = setTimeout(() => {
|
|
6846
|
-
this.run().catch((err) => {
|
|
6847
|
-
this.logger.warn('Failed to report stats', err);
|
|
6848
|
-
});
|
|
6849
|
-
}, timeout);
|
|
6850
|
-
};
|
|
6851
6878
|
this.sfuClient = sfuClient;
|
|
6852
6879
|
this.options = options;
|
|
6853
6880
|
this.subscriber = subscriber;
|
|
@@ -12092,9 +12119,7 @@ class SpeakerManager {
|
|
|
12092
12119
|
* @returns an Observable that will be updated if a device is connected or disconnected
|
|
12093
12120
|
*/
|
|
12094
12121
|
listDevices() {
|
|
12095
|
-
|
|
12096
|
-
throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
|
|
12097
|
-
}
|
|
12122
|
+
assertUnsupportedInReactNative();
|
|
12098
12123
|
return getAudioOutputDevices(this.call.tracer);
|
|
12099
12124
|
}
|
|
12100
12125
|
/**
|
|
@@ -12105,9 +12130,7 @@ class SpeakerManager {
|
|
|
12105
12130
|
* @param deviceId empty string means the system default
|
|
12106
12131
|
*/
|
|
12107
12132
|
select(deviceId) {
|
|
12108
|
-
|
|
12109
|
-
throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
|
|
12110
|
-
}
|
|
12133
|
+
assertUnsupportedInReactNative();
|
|
12111
12134
|
this.state.setDevice(deviceId);
|
|
12112
12135
|
}
|
|
12113
12136
|
/**
|
|
@@ -12117,9 +12140,7 @@ class SpeakerManager {
|
|
|
12117
12140
|
* Note: This method is not supported in React Native
|
|
12118
12141
|
*/
|
|
12119
12142
|
setVolume(volume) {
|
|
12120
|
-
|
|
12121
|
-
throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
|
|
12122
|
-
}
|
|
12143
|
+
assertUnsupportedInReactNative();
|
|
12123
12144
|
if (volume && (volume < 0 || volume > 1)) {
|
|
12124
12145
|
throw new Error('Volume must be between 0 and 1');
|
|
12125
12146
|
}
|
|
@@ -12146,6 +12167,11 @@ class SpeakerManager {
|
|
|
12146
12167
|
});
|
|
12147
12168
|
}
|
|
12148
12169
|
}
|
|
12170
|
+
const assertUnsupportedInReactNative = () => {
|
|
12171
|
+
if (isReactNative()) {
|
|
12172
|
+
throw new Error('Unsupported in React Native. See: https://getstream.io/video/docs/react-native/guides/camera-and-microphone/#speaker-management');
|
|
12173
|
+
}
|
|
12174
|
+
};
|
|
12149
12175
|
|
|
12150
12176
|
/**
|
|
12151
12177
|
* An object representation of a `Call`.
|
|
@@ -12448,6 +12474,8 @@ class Call {
|
|
|
12448
12474
|
}
|
|
12449
12475
|
this.statsReporter?.stop();
|
|
12450
12476
|
this.statsReporter = undefined;
|
|
12477
|
+
const leaveReason = message ?? reason ?? 'user is leaving the call';
|
|
12478
|
+
this.tracer.trace('call.leaveReason', leaveReason);
|
|
12451
12479
|
this.sfuStatsReporter?.flush();
|
|
12452
12480
|
this.sfuStatsReporter?.stop();
|
|
12453
12481
|
this.sfuStatsReporter = undefined;
|
|
@@ -12455,7 +12483,7 @@ class Call {
|
|
|
12455
12483
|
this.subscriber = undefined;
|
|
12456
12484
|
this.publisher?.dispose();
|
|
12457
12485
|
this.publisher = undefined;
|
|
12458
|
-
await this.sfuClient?.leaveAndClose(
|
|
12486
|
+
await this.sfuClient?.leaveAndClose(leaveReason);
|
|
12459
12487
|
this.sfuClient = undefined;
|
|
12460
12488
|
this.dynascaleManager.setSfuClient(undefined);
|
|
12461
12489
|
await this.dynascaleManager.dispose();
|
|
@@ -12597,6 +12625,7 @@ class Call {
|
|
|
12597
12625
|
* Unless you are implementing a custom "ringing" flow, you should not use this method.
|
|
12598
12626
|
*/
|
|
12599
12627
|
this.accept = async () => {
|
|
12628
|
+
this.tracer.trace('call.accept', '');
|
|
12600
12629
|
return this.streamClient.post(`${this.streamClientBasePath}/accept`);
|
|
12601
12630
|
};
|
|
12602
12631
|
/**
|
|
@@ -12609,6 +12638,7 @@ class Call {
|
|
|
12609
12638
|
* @param reason the reason for rejecting the call.
|
|
12610
12639
|
*/
|
|
12611
12640
|
this.reject = async (reason = 'decline') => {
|
|
12641
|
+
this.tracer.trace('call.reject', reason);
|
|
12612
12642
|
return this.streamClient.post(`${this.streamClientBasePath}/reject`, { reason: reason });
|
|
12613
12643
|
};
|
|
12614
12644
|
/**
|
|
@@ -13372,11 +13402,6 @@ class Call {
|
|
|
13372
13402
|
trackTypes.push(screenShareAudio);
|
|
13373
13403
|
}
|
|
13374
13404
|
}
|
|
13375
|
-
if (track.kind === 'video') {
|
|
13376
|
-
// schedules calibration report - the SFU will use the performance stats
|
|
13377
|
-
// to adjust the quality thresholds as early as possible
|
|
13378
|
-
this.sfuStatsReporter?.scheduleOne(3000);
|
|
13379
|
-
}
|
|
13380
13405
|
await this.updateLocalStreamState(mediaStream, ...trackTypes);
|
|
13381
13406
|
};
|
|
13382
13407
|
/**
|
|
@@ -15321,7 +15346,7 @@ class StreamClient {
|
|
|
15321
15346
|
this.getUserAgent = () => {
|
|
15322
15347
|
if (!this.cachedUserAgent) {
|
|
15323
15348
|
const { clientAppIdentifier = {} } = this.options;
|
|
15324
|
-
const { sdkName = 'js', sdkVersion = "1.41.
|
|
15349
|
+
const { sdkName = 'js', sdkVersion = "1.41.3", ...extras } = clientAppIdentifier;
|
|
15325
15350
|
this.cachedUserAgent = [
|
|
15326
15351
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
15327
15352
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|