@stream-io/video-client 1.47.0 → 1.48.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 +6 -0
- package/dist/index.browser.es.js +9 -192
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +8 -192
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.es.js +9 -192
- package/dist/index.es.js.map +1 -1
- package/dist/src/devices/MicrophoneManager.d.ts +0 -1
- package/dist/src/types.d.ts +11 -0
- package/index.ts +0 -1
- package/package.json +1 -1
- package/src/devices/MicrophoneManager.ts +9 -5
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +28 -29
- package/src/types.ts +9 -0
- package/dist/src/helpers/RNSpeechDetector.d.ts +0 -23
- package/src/helpers/RNSpeechDetector.ts +0 -224
- package/src/helpers/__tests__/RNSpeechDetector.test.ts +0 -52
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,5 @@ export * from './src/helpers/DynascaleManager';
|
|
|
19
19
|
export * from './src/helpers/ViewportTracker';
|
|
20
20
|
export * from './src/helpers/sound-detector';
|
|
21
21
|
export * from './src/helpers/participantUtils';
|
|
22
|
-
export * from './src/helpers/RNSpeechDetector';
|
|
23
22
|
export * as Browsers from './src/helpers/browsers';
|
|
24
23
|
export * from './src/logger';
|
package/dist/index.es.js
CHANGED
|
@@ -6285,7 +6285,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6285
6285
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6286
6286
|
};
|
|
6287
6287
|
|
|
6288
|
-
const version = "1.
|
|
6288
|
+
const version = "1.48.0";
|
|
6289
6289
|
const [major, minor, patch] = version.split('.');
|
|
6290
6290
|
let sdkInfo = {
|
|
6291
6291
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -11933,192 +11933,6 @@ const createNoAudioDetector = (audioStream, options) => {
|
|
|
11933
11933
|
return stop;
|
|
11934
11934
|
};
|
|
11935
11935
|
|
|
11936
|
-
class RNSpeechDetector {
|
|
11937
|
-
constructor(externalAudioStream) {
|
|
11938
|
-
this.pc1 = new RTCPeerConnection({});
|
|
11939
|
-
this.pc2 = new RTCPeerConnection({});
|
|
11940
|
-
this.isStopped = false;
|
|
11941
|
-
this.externalAudioStream = externalAudioStream;
|
|
11942
|
-
}
|
|
11943
|
-
/**
|
|
11944
|
-
* Starts the speech detection.
|
|
11945
|
-
*/
|
|
11946
|
-
async start(onSoundDetectedStateChanged) {
|
|
11947
|
-
let detachListeners;
|
|
11948
|
-
let unsubscribe;
|
|
11949
|
-
try {
|
|
11950
|
-
this.isStopped = false;
|
|
11951
|
-
const audioStream = this.externalAudioStream != null
|
|
11952
|
-
? this.externalAudioStream
|
|
11953
|
-
: await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
11954
|
-
this.audioStream = audioStream;
|
|
11955
|
-
const onPc1IceCandidate = (e) => {
|
|
11956
|
-
this.forwardIceCandidate(this.pc2, e.candidate);
|
|
11957
|
-
};
|
|
11958
|
-
const onPc2IceCandidate = (e) => {
|
|
11959
|
-
this.forwardIceCandidate(this.pc1, e.candidate);
|
|
11960
|
-
};
|
|
11961
|
-
const onTrackPc2 = (e) => {
|
|
11962
|
-
e.streams[0].getTracks().forEach((track) => {
|
|
11963
|
-
// In RN, the remote track is automatically added to the audio output device
|
|
11964
|
-
// so we need to mute it to avoid hearing the audio back
|
|
11965
|
-
// @ts-expect-error _setVolume is a private method in react-native-webrtc
|
|
11966
|
-
track._setVolume(0);
|
|
11967
|
-
});
|
|
11968
|
-
};
|
|
11969
|
-
this.pc1.addEventListener('icecandidate', onPc1IceCandidate);
|
|
11970
|
-
this.pc2.addEventListener('icecandidate', onPc2IceCandidate);
|
|
11971
|
-
this.pc2.addEventListener('track', onTrackPc2);
|
|
11972
|
-
detachListeners = () => {
|
|
11973
|
-
this.pc1.removeEventListener('icecandidate', onPc1IceCandidate);
|
|
11974
|
-
this.pc2.removeEventListener('icecandidate', onPc2IceCandidate);
|
|
11975
|
-
this.pc2.removeEventListener('track', onTrackPc2);
|
|
11976
|
-
};
|
|
11977
|
-
audioStream
|
|
11978
|
-
.getTracks()
|
|
11979
|
-
.forEach((track) => this.pc1.addTrack(track, audioStream));
|
|
11980
|
-
const offer = await this.pc1.createOffer({});
|
|
11981
|
-
await this.pc2.setRemoteDescription(offer);
|
|
11982
|
-
await this.pc1.setLocalDescription(offer);
|
|
11983
|
-
const answer = await this.pc2.createAnswer();
|
|
11984
|
-
await this.pc1.setRemoteDescription(answer);
|
|
11985
|
-
await this.pc2.setLocalDescription(answer);
|
|
11986
|
-
unsubscribe = this.onSpeakingDetectedStateChange(onSoundDetectedStateChanged);
|
|
11987
|
-
return () => {
|
|
11988
|
-
detachListeners?.();
|
|
11989
|
-
unsubscribe?.();
|
|
11990
|
-
this.stop();
|
|
11991
|
-
};
|
|
11992
|
-
}
|
|
11993
|
-
catch (error) {
|
|
11994
|
-
detachListeners?.();
|
|
11995
|
-
unsubscribe?.();
|
|
11996
|
-
this.stop();
|
|
11997
|
-
const logger = videoLoggerSystem.getLogger('RNSpeechDetector');
|
|
11998
|
-
logger.error('error handling permissions: ', error);
|
|
11999
|
-
return () => { };
|
|
12000
|
-
}
|
|
12001
|
-
}
|
|
12002
|
-
/**
|
|
12003
|
-
* Stops the speech detection and releases all allocated resources.
|
|
12004
|
-
*/
|
|
12005
|
-
stop() {
|
|
12006
|
-
if (this.isStopped)
|
|
12007
|
-
return;
|
|
12008
|
-
this.isStopped = true;
|
|
12009
|
-
this.pc1.close();
|
|
12010
|
-
this.pc2.close();
|
|
12011
|
-
if (this.externalAudioStream != null) {
|
|
12012
|
-
this.externalAudioStream = undefined;
|
|
12013
|
-
}
|
|
12014
|
-
else {
|
|
12015
|
-
this.cleanupAudioStream();
|
|
12016
|
-
}
|
|
12017
|
-
}
|
|
12018
|
-
/**
|
|
12019
|
-
* Public method that detects the audio levels and returns the status.
|
|
12020
|
-
*/
|
|
12021
|
-
onSpeakingDetectedStateChange(onSoundDetectedStateChanged) {
|
|
12022
|
-
const initialBaselineNoiseLevel = 0.13;
|
|
12023
|
-
let baselineNoiseLevel = initialBaselineNoiseLevel;
|
|
12024
|
-
let speechDetected = false;
|
|
12025
|
-
let speechTimer;
|
|
12026
|
-
let silenceTimer;
|
|
12027
|
-
const audioLevelHistory = []; // Store recent audio levels for smoother detection
|
|
12028
|
-
const historyLength = 10;
|
|
12029
|
-
const silenceThreshold = 1.1;
|
|
12030
|
-
const resetThreshold = 0.9;
|
|
12031
|
-
const speechTimeout = 500; // Speech is set to true after 500ms of audio detection
|
|
12032
|
-
const silenceTimeout = 5000; // Reset baseline after 5 seconds of silence
|
|
12033
|
-
const checkAudioLevel = async () => {
|
|
12034
|
-
try {
|
|
12035
|
-
const stats = await this.pc1.getStats();
|
|
12036
|
-
const report = flatten(stats);
|
|
12037
|
-
// Audio levels are present inside stats of type `media-source` and of kind `audio`
|
|
12038
|
-
const audioMediaSourceStats = report.find((stat) => stat.type === 'media-source' &&
|
|
12039
|
-
stat.kind === 'audio');
|
|
12040
|
-
if (audioMediaSourceStats) {
|
|
12041
|
-
const { audioLevel } = audioMediaSourceStats;
|
|
12042
|
-
if (audioLevel) {
|
|
12043
|
-
// Update audio level history (with max historyLength sized array)
|
|
12044
|
-
audioLevelHistory.push(audioLevel);
|
|
12045
|
-
if (audioLevelHistory.length > historyLength) {
|
|
12046
|
-
audioLevelHistory.shift();
|
|
12047
|
-
}
|
|
12048
|
-
// Calculate average audio level
|
|
12049
|
-
const avgAudioLevel = audioLevelHistory.reduce((a, b) => a + b, 0) /
|
|
12050
|
-
audioLevelHistory.length;
|
|
12051
|
-
// Update baseline (if necessary) based on silence detection
|
|
12052
|
-
if (avgAudioLevel < baselineNoiseLevel * silenceThreshold) {
|
|
12053
|
-
if (!silenceTimer) {
|
|
12054
|
-
silenceTimer = setTimeout(() => {
|
|
12055
|
-
baselineNoiseLevel = Math.min(avgAudioLevel * resetThreshold, initialBaselineNoiseLevel);
|
|
12056
|
-
}, silenceTimeout);
|
|
12057
|
-
}
|
|
12058
|
-
}
|
|
12059
|
-
else {
|
|
12060
|
-
clearTimeout(silenceTimer);
|
|
12061
|
-
silenceTimer = undefined;
|
|
12062
|
-
}
|
|
12063
|
-
// Speech detection with hysteresis
|
|
12064
|
-
if (avgAudioLevel > baselineNoiseLevel * 1.5) {
|
|
12065
|
-
if (!speechDetected) {
|
|
12066
|
-
speechDetected = true;
|
|
12067
|
-
onSoundDetectedStateChanged({
|
|
12068
|
-
isSoundDetected: true,
|
|
12069
|
-
audioLevel,
|
|
12070
|
-
});
|
|
12071
|
-
}
|
|
12072
|
-
clearTimeout(speechTimer);
|
|
12073
|
-
speechTimer = setTimeout(() => {
|
|
12074
|
-
speechDetected = false;
|
|
12075
|
-
onSoundDetectedStateChanged({
|
|
12076
|
-
isSoundDetected: false,
|
|
12077
|
-
audioLevel: 0,
|
|
12078
|
-
});
|
|
12079
|
-
}, speechTimeout);
|
|
12080
|
-
}
|
|
12081
|
-
}
|
|
12082
|
-
}
|
|
12083
|
-
}
|
|
12084
|
-
catch (error) {
|
|
12085
|
-
const logger = videoLoggerSystem.getLogger('RNSpeechDetector');
|
|
12086
|
-
logger.error('error checking audio level from stats', error);
|
|
12087
|
-
}
|
|
12088
|
-
};
|
|
12089
|
-
const intervalId = setInterval(checkAudioLevel, 250);
|
|
12090
|
-
return () => {
|
|
12091
|
-
clearInterval(intervalId);
|
|
12092
|
-
clearTimeout(speechTimer);
|
|
12093
|
-
clearTimeout(silenceTimer);
|
|
12094
|
-
};
|
|
12095
|
-
}
|
|
12096
|
-
cleanupAudioStream() {
|
|
12097
|
-
if (!this.audioStream) {
|
|
12098
|
-
return;
|
|
12099
|
-
}
|
|
12100
|
-
this.audioStream.getTracks().forEach((track) => track.stop());
|
|
12101
|
-
if (
|
|
12102
|
-
// @ts-expect-error release() is present in react-native-webrtc
|
|
12103
|
-
typeof this.audioStream.release === 'function') {
|
|
12104
|
-
// @ts-expect-error called to dispose the stream in RN
|
|
12105
|
-
this.audioStream.release();
|
|
12106
|
-
}
|
|
12107
|
-
}
|
|
12108
|
-
forwardIceCandidate(destination, candidate) {
|
|
12109
|
-
if (this.isStopped ||
|
|
12110
|
-
!candidate ||
|
|
12111
|
-
destination.signalingState === 'closed') {
|
|
12112
|
-
return;
|
|
12113
|
-
}
|
|
12114
|
-
destination.addIceCandidate(candidate).catch(() => {
|
|
12115
|
-
// silently ignore the error
|
|
12116
|
-
const logger = videoLoggerSystem.getLogger('RNSpeechDetector');
|
|
12117
|
-
logger.info('cannot add ice candidate - ignoring');
|
|
12118
|
-
});
|
|
12119
|
-
}
|
|
12120
|
-
}
|
|
12121
|
-
|
|
12122
11936
|
class MicrophoneManager extends AudioDeviceManager {
|
|
12123
11937
|
constructor(call, devicePersistence, disableMode = 'stop-tracks') {
|
|
12124
11938
|
super(call, new MicrophoneManagerState(disableMode, call.tracer), TrackType.AUDIO, devicePersistence);
|
|
@@ -12431,13 +12245,16 @@ class MicrophoneManager extends AudioDeviceManager {
|
|
|
12431
12245
|
return;
|
|
12432
12246
|
await this.teardownSpeakingWhileMutedDetection();
|
|
12433
12247
|
if (isReactNative()) {
|
|
12434
|
-
|
|
12435
|
-
|
|
12248
|
+
const speechActivity = globalThis.streamRNVideoSDK?.nativeEvents?.speechActivity;
|
|
12249
|
+
if (!speechActivity) {
|
|
12250
|
+
this.logger.warn('Native speech activity not available, make sure the "@stream-io/react-native-webrtc" peer dependency version is satisfied');
|
|
12251
|
+
return;
|
|
12252
|
+
}
|
|
12253
|
+
const unsubscribe = speechActivity.subscribe((event) => {
|
|
12436
12254
|
this.state.setSpeakingWhileMuted(event.isSoundDetected);
|
|
12437
12255
|
});
|
|
12438
12256
|
this.soundDetectorCleanup = async () => {
|
|
12439
12257
|
unsubscribe();
|
|
12440
|
-
this.rnSpeechDetector = undefined;
|
|
12441
12258
|
};
|
|
12442
12259
|
}
|
|
12443
12260
|
else {
|
|
@@ -16150,7 +15967,7 @@ class StreamClient {
|
|
|
16150
15967
|
this.getUserAgent = () => {
|
|
16151
15968
|
if (!this.cachedUserAgent) {
|
|
16152
15969
|
const { clientAppIdentifier = {} } = this.options;
|
|
16153
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
15970
|
+
const { sdkName = 'js', sdkVersion = "1.48.0", ...extras } = clientAppIdentifier;
|
|
16154
15971
|
this.cachedUserAgent = [
|
|
16155
15972
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
16156
15973
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -16786,5 +16603,5 @@ const humanize = (n) => {
|
|
|
16786
16603
|
return String(n);
|
|
16787
16604
|
};
|
|
16788
16605
|
|
|
16789
|
-
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability,
|
|
16606
|
+
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RawRecordingSettingsRequestModeEnum, RawRecordingSettingsResponseModeEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, SfuJoinError, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio$1 as hasAudio, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, humanize, isPinned, livestreamOrAudioRoomSortPreset, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, videoLoggerSystem, withParticipantSource };
|
|
16790
16607
|
//# sourceMappingURL=index.es.js.map
|