@stream-io/video-client 1.42.3 → 1.43.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 +13 -0
- package/dist/index.browser.es.js +89 -20
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +90 -21
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +89 -20
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +41 -1
- package/dist/src/devices/MicrophoneManager.d.ts +2 -0
- package/dist/src/helpers/no-audio-detector.d.ts +1 -7
- package/package.json +1 -1
- package/src/Call.ts +76 -0
- package/src/devices/MicrophoneManager.ts +21 -5
- package/src/devices/__tests__/MicrophoneManager.test.ts +52 -0
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +26 -1
- package/src/devices/__tests__/web-audio.mocks.ts +6 -2
- package/src/helpers/__tests__/no-audio-detector.test.ts +54 -28
- package/src/helpers/no-audio-detector.ts +25 -20
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.43.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.42.3...@stream-io/video-client-1.43.0) (2026-02-20)
|
|
6
|
+
|
|
7
|
+
- **client:** trace updatePublishOptions overrides ([#2136](https://github.com/GetStream/stream-video-js/issues/2136)) ([bcc1e92](https://github.com/GetStream/stream-video-js/commit/bcc1e92ac89374324a57d1df85be38a2661a4c53))
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- **client:** add list recording APIs and deprecate query methods ([#2135](https://github.com/GetStream/stream-video-js/issues/2135)) ([5331cb5](https://github.com/GetStream/stream-video-js/commit/5331cb5205466dc052c729fb07d84209208af362))
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
- **client:** harden flat-line no-audio detection ([#2131](https://github.com/GetStream/stream-video-js/issues/2131)) ([9c2aa22](https://github.com/GetStream/stream-video-js/commit/9c2aa222b189c5e24510430dfddbf164555abf1c))
|
|
16
|
+
- **client:** prevent stale speaking-while-muted detector ([#2130](https://github.com/GetStream/stream-video-js/issues/2130)) ([e5c408d](https://github.com/GetStream/stream-video-js/commit/e5c408d73de1b8f20e775642b0b19eb0ffd979a8))
|
|
17
|
+
|
|
5
18
|
## [1.42.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.42.2...@stream-io/video-client-1.42.3) (2026-02-16)
|
|
6
19
|
|
|
7
20
|
### Bug Fixes
|
package/dist/index.browser.es.js
CHANGED
|
@@ -6231,7 +6231,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6231
6231
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6232
6232
|
};
|
|
6233
6233
|
|
|
6234
|
-
const version = "1.
|
|
6234
|
+
const version = "1.43.0";
|
|
6235
6235
|
const [major, minor, patch] = version.split('.');
|
|
6236
6236
|
let sdkInfo = {
|
|
6237
6237
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -11480,12 +11480,21 @@ const createSoundDetector = (audioStream, onSoundDetectedStateChanged, options =
|
|
|
11480
11480
|
};
|
|
11481
11481
|
|
|
11482
11482
|
/**
|
|
11483
|
-
* Analyzes
|
|
11483
|
+
* Analyzes time-domain waveform data to determine if audio is being captured.
|
|
11484
|
+
* Uses the waveform RMS around the 128 midpoint for robust silence detection.
|
|
11484
11485
|
*/
|
|
11485
|
-
const hasAudio = (analyser
|
|
11486
|
-
const data = new Uint8Array(analyser.
|
|
11487
|
-
analyser.
|
|
11488
|
-
|
|
11486
|
+
const hasAudio = (analyser) => {
|
|
11487
|
+
const data = new Uint8Array(analyser.fftSize);
|
|
11488
|
+
analyser.getByteTimeDomainData(data);
|
|
11489
|
+
let squareSum = 0;
|
|
11490
|
+
for (const sample of data) {
|
|
11491
|
+
const centered = sample - 128;
|
|
11492
|
+
// Ignore tiny quantization/jitter around midpoint (e.g. 127/128 samples).
|
|
11493
|
+
const signal = Math.abs(centered) <= 1 ? 0 : centered;
|
|
11494
|
+
squareSum += signal * signal;
|
|
11495
|
+
}
|
|
11496
|
+
const rms = Math.sqrt(squareSum / data.length);
|
|
11497
|
+
return rms > 0;
|
|
11489
11498
|
};
|
|
11490
11499
|
/** Helper for "no event" transitions */
|
|
11491
11500
|
const noEmit = (nextState) => ({
|
|
@@ -11499,9 +11508,9 @@ const emit = (capturesAudio, nextState) => ({ shouldEmit: true, nextState, captu
|
|
|
11499
11508
|
*/
|
|
11500
11509
|
const transitionState = (state, audioDetected, options) => {
|
|
11501
11510
|
if (audioDetected) {
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11511
|
+
// Any observed audio means the microphone is capturing.
|
|
11512
|
+
// Emit recovery/success and let the caller stop the detector.
|
|
11513
|
+
return emit(true, { kind: 'IDLE' });
|
|
11505
11514
|
}
|
|
11506
11515
|
const { noAudioThresholdMs, emitIntervalMs } = options;
|
|
11507
11516
|
const now = Date.now();
|
|
@@ -11542,16 +11551,17 @@ const createAudioAnalyzer = (audioStream, fftSize) => {
|
|
|
11542
11551
|
* @returns a cleanup function which once invoked stops the no-audio detector.
|
|
11543
11552
|
*/
|
|
11544
11553
|
const createNoAudioDetector = (audioStream, options) => {
|
|
11545
|
-
const { detectionFrequencyInMs = 350,
|
|
11554
|
+
const { detectionFrequencyInMs = 350, fftSize = 512, onCaptureStatusChange, } = options;
|
|
11546
11555
|
let state = { kind: 'IDLE' };
|
|
11547
11556
|
const { audioContext, analyser } = createAudioAnalyzer(audioStream, fftSize);
|
|
11548
11557
|
const detectionIntervalId = setInterval(() => {
|
|
11549
|
-
const [
|
|
11550
|
-
if (
|
|
11558
|
+
const [track] = audioStream.getAudioTracks();
|
|
11559
|
+
if (track && !track.enabled) {
|
|
11551
11560
|
state = { kind: 'IDLE' };
|
|
11552
11561
|
return;
|
|
11553
11562
|
}
|
|
11554
|
-
|
|
11563
|
+
// Missing or ended track is treated as no-audio to surface abrupt capture loss.
|
|
11564
|
+
const audioDetected = track?.readyState === 'live' && hasAudio(analyser);
|
|
11555
11565
|
const transition = transitionState(state, audioDetected, options);
|
|
11556
11566
|
state = transition.nextState;
|
|
11557
11567
|
if (!transition.shouldEmit)
|
|
@@ -12024,6 +12034,9 @@ class MicrophoneManager extends AudioDeviceManager {
|
|
|
12024
12034
|
}
|
|
12025
12035
|
async startSpeakingWhileMutedDetection(deviceId) {
|
|
12026
12036
|
await withoutConcurrency(this.soundDetectorConcurrencyTag, async () => {
|
|
12037
|
+
if (this.soundDetectorCleanup && this.soundDetectorDeviceId === deviceId)
|
|
12038
|
+
return;
|
|
12039
|
+
await this.teardownSpeakingWhileMutedDetection();
|
|
12027
12040
|
if (isReactNative()) {
|
|
12028
12041
|
this.rnSpeechDetector = new RNSpeechDetector();
|
|
12029
12042
|
const unsubscribe = await this.rnSpeechDetector.start((event) => {
|
|
@@ -12044,16 +12057,23 @@ class MicrophoneManager extends AudioDeviceManager {
|
|
|
12044
12057
|
this.state.setSpeakingWhileMuted(event.isSoundDetected);
|
|
12045
12058
|
});
|
|
12046
12059
|
}
|
|
12060
|
+
this.soundDetectorDeviceId = deviceId;
|
|
12047
12061
|
});
|
|
12048
12062
|
}
|
|
12049
12063
|
async stopSpeakingWhileMutedDetection() {
|
|
12050
12064
|
await withoutConcurrency(this.soundDetectorConcurrencyTag, async () => {
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12065
|
+
return this.teardownSpeakingWhileMutedDetection();
|
|
12066
|
+
});
|
|
12067
|
+
}
|
|
12068
|
+
async teardownSpeakingWhileMutedDetection() {
|
|
12069
|
+
const soundDetectorCleanup = this.soundDetectorCleanup;
|
|
12070
|
+
this.soundDetectorCleanup = undefined;
|
|
12071
|
+
this.soundDetectorDeviceId = undefined;
|
|
12072
|
+
this.state.setSpeakingWhileMuted(false);
|
|
12073
|
+
if (!soundDetectorCleanup)
|
|
12074
|
+
return;
|
|
12075
|
+
await soundDetectorCleanup().catch((err) => {
|
|
12076
|
+
this.logger.warn('Failed to stop speaking while muted detector', err);
|
|
12057
12077
|
});
|
|
12058
12078
|
}
|
|
12059
12079
|
async hasPermission(permissionState) {
|
|
@@ -13706,6 +13726,7 @@ class Call {
|
|
|
13706
13726
|
if (this.state.callingState === CallingState.JOINED) {
|
|
13707
13727
|
this.logger.warn('Updating publish options after joining the call does not have an effect');
|
|
13708
13728
|
}
|
|
13729
|
+
this.tracer.trace('updatePublishOptions', options);
|
|
13709
13730
|
this.clientPublishOptions = { ...this.clientPublishOptions, ...options };
|
|
13710
13731
|
};
|
|
13711
13732
|
/**
|
|
@@ -14131,6 +14152,15 @@ class Call {
|
|
|
14131
14152
|
type: this.type,
|
|
14132
14153
|
});
|
|
14133
14154
|
};
|
|
14155
|
+
/**
|
|
14156
|
+
* Query call participants with optional filters.
|
|
14157
|
+
*
|
|
14158
|
+
* @param data the request data.
|
|
14159
|
+
* @param params optional query parameters.
|
|
14160
|
+
*/
|
|
14161
|
+
this.queryParticipants = async (data = {}, params = {}) => {
|
|
14162
|
+
return this.streamClient.post(`${this.streamClientBasePath}/participants`, data, params);
|
|
14163
|
+
};
|
|
14134
14164
|
/**
|
|
14135
14165
|
* Will update the call members.
|
|
14136
14166
|
*
|
|
@@ -14185,20 +14215,59 @@ class Call {
|
|
|
14185
14215
|
* Otherwise, all recordings for the current call will be returned.
|
|
14186
14216
|
*
|
|
14187
14217
|
* @param callSessionId the call session id to retrieve recordings for.
|
|
14218
|
+
* @deprecated use {@link listRecordings} instead.
|
|
14188
14219
|
*/
|
|
14189
14220
|
this.queryRecordings = async (callSessionId) => {
|
|
14221
|
+
return this.listRecordings(callSessionId);
|
|
14222
|
+
};
|
|
14223
|
+
/**
|
|
14224
|
+
* Retrieves the list of recordings for the current call or call session.
|
|
14225
|
+
*
|
|
14226
|
+
* If `callSessionId` is provided, it will return the recordings for that call session.
|
|
14227
|
+
* Otherwise, all recordings for the current call will be returned.
|
|
14228
|
+
*
|
|
14229
|
+
* @param callSessionId the call session id to retrieve recordings for.
|
|
14230
|
+
*/
|
|
14231
|
+
this.listRecordings = async (callSessionId) => {
|
|
14190
14232
|
let endpoint = this.streamClientBasePath;
|
|
14191
14233
|
if (callSessionId) {
|
|
14192
14234
|
endpoint = `${endpoint}/${callSessionId}`;
|
|
14193
14235
|
}
|
|
14194
14236
|
return this.streamClient.get(`${endpoint}/recordings`);
|
|
14195
14237
|
};
|
|
14238
|
+
/**
|
|
14239
|
+
* Deletes a recording for the given call session.
|
|
14240
|
+
*
|
|
14241
|
+
* @param callSessionId the call session id that the recording belongs to.
|
|
14242
|
+
* @param filename the recording filename.
|
|
14243
|
+
*/
|
|
14244
|
+
this.deleteRecording = async (callSessionId, filename) => {
|
|
14245
|
+
return this.streamClient.delete(`${this.streamClientBasePath}/${encodeURIComponent(callSessionId)}/recordings/${encodeURIComponent(filename)}`);
|
|
14246
|
+
};
|
|
14247
|
+
/**
|
|
14248
|
+
* Deletes a transcription for the given call session.
|
|
14249
|
+
*
|
|
14250
|
+
* @param callSessionId the call session id that the transcription belongs to.
|
|
14251
|
+
* @param filename the transcription filename.
|
|
14252
|
+
*/
|
|
14253
|
+
this.deleteTranscription = async (callSessionId, filename) => {
|
|
14254
|
+
return this.streamClient.delete(`${this.streamClientBasePath}/${encodeURIComponent(callSessionId)}/transcriptions/${encodeURIComponent(filename)}`);
|
|
14255
|
+
};
|
|
14196
14256
|
/**
|
|
14197
14257
|
* Retrieves the list of transcriptions for the current call.
|
|
14198
14258
|
*
|
|
14199
14259
|
* @returns the list of transcriptions.
|
|
14260
|
+
* @deprecated use {@link listTranscriptions} instead.
|
|
14200
14261
|
*/
|
|
14201
14262
|
this.queryTranscriptions = async () => {
|
|
14263
|
+
return this.listTranscriptions();
|
|
14264
|
+
};
|
|
14265
|
+
/**
|
|
14266
|
+
* Retrieves the list of transcriptions for the current call.
|
|
14267
|
+
*
|
|
14268
|
+
* @returns the list of transcriptions.
|
|
14269
|
+
*/
|
|
14270
|
+
this.listTranscriptions = async () => {
|
|
14202
14271
|
return this.streamClient.get(`${this.streamClientBasePath}/transcriptions`);
|
|
14203
14272
|
};
|
|
14204
14273
|
/**
|
|
@@ -15601,7 +15670,7 @@ class StreamClient {
|
|
|
15601
15670
|
this.getUserAgent = () => {
|
|
15602
15671
|
if (!this.cachedUserAgent) {
|
|
15603
15672
|
const { clientAppIdentifier = {} } = this.options;
|
|
15604
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
15673
|
+
const { sdkName = 'js', sdkVersion = "1.43.0", ...extras } = clientAppIdentifier;
|
|
15605
15674
|
this.cachedUserAgent = [
|
|
15606
15675
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
15607
15676
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|