@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/dist/index.cjs.js
CHANGED
|
@@ -6208,7 +6208,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6208
6208
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6209
6209
|
};
|
|
6210
6210
|
|
|
6211
|
-
const version = "1.41.
|
|
6211
|
+
const version = "1.41.3";
|
|
6212
6212
|
const [major, minor, patch] = version.split('.');
|
|
6213
6213
|
let sdkInfo = {
|
|
6214
6214
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -6531,6 +6531,16 @@ const createStatsReporter = ({ subscriber, publisher, state, datacenter, polling
|
|
|
6531
6531
|
stop,
|
|
6532
6532
|
};
|
|
6533
6533
|
};
|
|
6534
|
+
/**
|
|
6535
|
+
* Extracts camera statistics from a media source.
|
|
6536
|
+
*
|
|
6537
|
+
* @param mediaSource the media source stats to extract camera info from.
|
|
6538
|
+
*/
|
|
6539
|
+
const getCameraStats = (mediaSource) => ({
|
|
6540
|
+
frameRate: mediaSource.framesPerSecond,
|
|
6541
|
+
frameWidth: mediaSource.width,
|
|
6542
|
+
frameHeight: mediaSource.height,
|
|
6543
|
+
});
|
|
6534
6544
|
/**
|
|
6535
6545
|
* Transforms raw RTC stats into a slimmer and uniform across browsers format.
|
|
6536
6546
|
*
|
|
@@ -6556,6 +6566,7 @@ const transform = (report, opts) => {
|
|
|
6556
6566
|
}
|
|
6557
6567
|
let trackType;
|
|
6558
6568
|
let audioLevel;
|
|
6569
|
+
let camera;
|
|
6559
6570
|
let concealedSamples;
|
|
6560
6571
|
let concealmentEvents;
|
|
6561
6572
|
let packetsReceived;
|
|
@@ -6571,6 +6582,9 @@ const transform = (report, opts) => {
|
|
|
6571
6582
|
typeof mediaSource.audioLevel === 'number') {
|
|
6572
6583
|
audioLevel = mediaSource.audioLevel;
|
|
6573
6584
|
}
|
|
6585
|
+
if (trackKind === 'video') {
|
|
6586
|
+
camera = getCameraStats(mediaSource);
|
|
6587
|
+
}
|
|
6574
6588
|
}
|
|
6575
6589
|
}
|
|
6576
6590
|
else if (kind === 'subscriber' && trackKind === 'audio') {
|
|
@@ -6604,6 +6618,7 @@ const transform = (report, opts) => {
|
|
|
6604
6618
|
concealmentEvents,
|
|
6605
6619
|
packetsReceived,
|
|
6606
6620
|
packetsLost,
|
|
6621
|
+
camera,
|
|
6607
6622
|
};
|
|
6608
6623
|
});
|
|
6609
6624
|
return {
|
|
@@ -6623,6 +6638,7 @@ const getEmptyVideoStats = (stats) => {
|
|
|
6623
6638
|
highestFrameWidth: 0,
|
|
6624
6639
|
highestFrameHeight: 0,
|
|
6625
6640
|
highestFramesPerSecond: 0,
|
|
6641
|
+
camera: {},
|
|
6626
6642
|
codec: '',
|
|
6627
6643
|
codecPerTrackType: {},
|
|
6628
6644
|
timestamp: Date.now(),
|
|
@@ -6667,6 +6683,9 @@ const aggregate = (stats) => {
|
|
|
6667
6683
|
acc.highestFramesPerSecond = stream.framesPerSecond || 0;
|
|
6668
6684
|
maxArea = streamArea;
|
|
6669
6685
|
}
|
|
6686
|
+
if (stream.trackType === TrackType.VIDEO) {
|
|
6687
|
+
acc.camera = stream.camera;
|
|
6688
|
+
}
|
|
6670
6689
|
qualityLimitationReasons.add(stream.qualityLimitationReason || '');
|
|
6671
6690
|
return acc;
|
|
6672
6691
|
}, aggregatedStats);
|
|
@@ -6731,6 +6750,7 @@ const aggregateAudio = (stats) => {
|
|
|
6731
6750
|
class SfuStatsReporter {
|
|
6732
6751
|
constructor(sfuClient, { options, clientDetails, subscriber, publisher, microphone, camera, state, tracer, unifiedSessionId, }) {
|
|
6733
6752
|
this.logger = videoLoggerSystem.getLogger('SfuStatsReporter');
|
|
6753
|
+
this.reportCount = 0;
|
|
6734
6754
|
this.inputDevices = new Map();
|
|
6735
6755
|
this.observeDevice = (device, kind) => {
|
|
6736
6756
|
const { browserPermissionState$ } = device.state;
|
|
@@ -6832,17 +6852,31 @@ class SfuStatsReporter {
|
|
|
6832
6852
|
throw err;
|
|
6833
6853
|
}
|
|
6834
6854
|
};
|
|
6855
|
+
this.scheduleNextReport = () => {
|
|
6856
|
+
const intervals = [1500, 3000, 3000, 5000];
|
|
6857
|
+
if (this.reportCount < intervals.length) {
|
|
6858
|
+
this.timeoutId = setTimeout(() => {
|
|
6859
|
+
this.flush();
|
|
6860
|
+
this.reportCount++;
|
|
6861
|
+
this.scheduleNextReport();
|
|
6862
|
+
}, intervals[this.reportCount]);
|
|
6863
|
+
}
|
|
6864
|
+
else {
|
|
6865
|
+
clearInterval(this.intervalId);
|
|
6866
|
+
this.intervalId = setInterval(() => {
|
|
6867
|
+
this.flush();
|
|
6868
|
+
}, this.options.reporting_interval_ms);
|
|
6869
|
+
}
|
|
6870
|
+
};
|
|
6835
6871
|
this.start = () => {
|
|
6836
6872
|
if (this.options.reporting_interval_ms <= 0)
|
|
6837
6873
|
return;
|
|
6838
6874
|
this.observeDevice(this.microphone, 'mic');
|
|
6839
6875
|
this.observeDevice(this.camera, 'camera');
|
|
6876
|
+
this.reportCount = 0;
|
|
6840
6877
|
clearInterval(this.intervalId);
|
|
6841
|
-
this.
|
|
6842
|
-
|
|
6843
|
-
this.logger.warn('Failed to report stats', err);
|
|
6844
|
-
});
|
|
6845
|
-
}, this.options.reporting_interval_ms);
|
|
6878
|
+
clearTimeout(this.timeoutId);
|
|
6879
|
+
this.scheduleNextReport();
|
|
6846
6880
|
};
|
|
6847
6881
|
this.stop = () => {
|
|
6848
6882
|
this.unsubscribeDevicePermissionsSubscription?.();
|
|
@@ -6854,20 +6888,13 @@ class SfuStatsReporter {
|
|
|
6854
6888
|
this.intervalId = undefined;
|
|
6855
6889
|
clearTimeout(this.timeoutId);
|
|
6856
6890
|
this.timeoutId = undefined;
|
|
6891
|
+
this.reportCount = 0;
|
|
6857
6892
|
};
|
|
6858
6893
|
this.flush = () => {
|
|
6859
6894
|
this.run().catch((err) => {
|
|
6860
6895
|
this.logger.warn('Failed to flush report stats', err);
|
|
6861
6896
|
});
|
|
6862
6897
|
};
|
|
6863
|
-
this.scheduleOne = (timeout) => {
|
|
6864
|
-
clearTimeout(this.timeoutId);
|
|
6865
|
-
this.timeoutId = setTimeout(() => {
|
|
6866
|
-
this.run().catch((err) => {
|
|
6867
|
-
this.logger.warn('Failed to report stats', err);
|
|
6868
|
-
});
|
|
6869
|
-
}, timeout);
|
|
6870
|
-
};
|
|
6871
6898
|
this.sfuClient = sfuClient;
|
|
6872
6899
|
this.options = options;
|
|
6873
6900
|
this.subscriber = subscriber;
|
|
@@ -12112,9 +12139,7 @@ class SpeakerManager {
|
|
|
12112
12139
|
* @returns an Observable that will be updated if a device is connected or disconnected
|
|
12113
12140
|
*/
|
|
12114
12141
|
listDevices() {
|
|
12115
|
-
|
|
12116
|
-
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');
|
|
12117
|
-
}
|
|
12142
|
+
assertUnsupportedInReactNative();
|
|
12118
12143
|
return getAudioOutputDevices(this.call.tracer);
|
|
12119
12144
|
}
|
|
12120
12145
|
/**
|
|
@@ -12125,9 +12150,7 @@ class SpeakerManager {
|
|
|
12125
12150
|
* @param deviceId empty string means the system default
|
|
12126
12151
|
*/
|
|
12127
12152
|
select(deviceId) {
|
|
12128
|
-
|
|
12129
|
-
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');
|
|
12130
|
-
}
|
|
12153
|
+
assertUnsupportedInReactNative();
|
|
12131
12154
|
this.state.setDevice(deviceId);
|
|
12132
12155
|
}
|
|
12133
12156
|
/**
|
|
@@ -12137,9 +12160,7 @@ class SpeakerManager {
|
|
|
12137
12160
|
* Note: This method is not supported in React Native
|
|
12138
12161
|
*/
|
|
12139
12162
|
setVolume(volume) {
|
|
12140
|
-
|
|
12141
|
-
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');
|
|
12142
|
-
}
|
|
12163
|
+
assertUnsupportedInReactNative();
|
|
12143
12164
|
if (volume && (volume < 0 || volume > 1)) {
|
|
12144
12165
|
throw new Error('Volume must be between 0 and 1');
|
|
12145
12166
|
}
|
|
@@ -12166,6 +12187,11 @@ class SpeakerManager {
|
|
|
12166
12187
|
});
|
|
12167
12188
|
}
|
|
12168
12189
|
}
|
|
12190
|
+
const assertUnsupportedInReactNative = () => {
|
|
12191
|
+
if (isReactNative()) {
|
|
12192
|
+
throw new Error('Unsupported in React Native. See: https://getstream.io/video/docs/react-native/guides/camera-and-microphone/#speaker-management');
|
|
12193
|
+
}
|
|
12194
|
+
};
|
|
12169
12195
|
|
|
12170
12196
|
/**
|
|
12171
12197
|
* An object representation of a `Call`.
|
|
@@ -12468,6 +12494,8 @@ class Call {
|
|
|
12468
12494
|
}
|
|
12469
12495
|
this.statsReporter?.stop();
|
|
12470
12496
|
this.statsReporter = undefined;
|
|
12497
|
+
const leaveReason = message ?? reason ?? 'user is leaving the call';
|
|
12498
|
+
this.tracer.trace('call.leaveReason', leaveReason);
|
|
12471
12499
|
this.sfuStatsReporter?.flush();
|
|
12472
12500
|
this.sfuStatsReporter?.stop();
|
|
12473
12501
|
this.sfuStatsReporter = undefined;
|
|
@@ -12475,7 +12503,7 @@ class Call {
|
|
|
12475
12503
|
this.subscriber = undefined;
|
|
12476
12504
|
this.publisher?.dispose();
|
|
12477
12505
|
this.publisher = undefined;
|
|
12478
|
-
await this.sfuClient?.leaveAndClose(
|
|
12506
|
+
await this.sfuClient?.leaveAndClose(leaveReason);
|
|
12479
12507
|
this.sfuClient = undefined;
|
|
12480
12508
|
this.dynascaleManager.setSfuClient(undefined);
|
|
12481
12509
|
await this.dynascaleManager.dispose();
|
|
@@ -12617,6 +12645,7 @@ class Call {
|
|
|
12617
12645
|
* Unless you are implementing a custom "ringing" flow, you should not use this method.
|
|
12618
12646
|
*/
|
|
12619
12647
|
this.accept = async () => {
|
|
12648
|
+
this.tracer.trace('call.accept', '');
|
|
12620
12649
|
return this.streamClient.post(`${this.streamClientBasePath}/accept`);
|
|
12621
12650
|
};
|
|
12622
12651
|
/**
|
|
@@ -12629,6 +12658,7 @@ class Call {
|
|
|
12629
12658
|
* @param reason the reason for rejecting the call.
|
|
12630
12659
|
*/
|
|
12631
12660
|
this.reject = async (reason = 'decline') => {
|
|
12661
|
+
this.tracer.trace('call.reject', reason);
|
|
12632
12662
|
return this.streamClient.post(`${this.streamClientBasePath}/reject`, { reason: reason });
|
|
12633
12663
|
};
|
|
12634
12664
|
/**
|
|
@@ -13392,11 +13422,6 @@ class Call {
|
|
|
13392
13422
|
trackTypes.push(screenShareAudio);
|
|
13393
13423
|
}
|
|
13394
13424
|
}
|
|
13395
|
-
if (track.kind === 'video') {
|
|
13396
|
-
// schedules calibration report - the SFU will use the performance stats
|
|
13397
|
-
// to adjust the quality thresholds as early as possible
|
|
13398
|
-
this.sfuStatsReporter?.scheduleOne(3000);
|
|
13399
|
-
}
|
|
13400
13425
|
await this.updateLocalStreamState(mediaStream, ...trackTypes);
|
|
13401
13426
|
};
|
|
13402
13427
|
/**
|
|
@@ -15339,7 +15364,7 @@ class StreamClient {
|
|
|
15339
15364
|
this.getUserAgent = () => {
|
|
15340
15365
|
if (!this.cachedUserAgent) {
|
|
15341
15366
|
const { clientAppIdentifier = {} } = this.options;
|
|
15342
|
-
const { sdkName = 'js', sdkVersion = "1.41.
|
|
15367
|
+
const { sdkName = 'js', sdkVersion = "1.41.3", ...extras } = clientAppIdentifier;
|
|
15343
15368
|
this.cachedUserAgent = [
|
|
15344
15369
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
15345
15370
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|