@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/dist/index.cjs.js CHANGED
@@ -6304,7 +6304,7 @@ const getSdkVersion = (sdk) => {
6304
6304
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6305
6305
  };
6306
6306
 
6307
- const version = "1.47.0";
6307
+ const version = "1.48.0";
6308
6308
  const [major, minor, patch] = version.split('.');
6309
6309
  let sdkInfo = {
6310
6310
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -11952,192 +11952,6 @@ const createNoAudioDetector = (audioStream, options) => {
11952
11952
  return stop;
11953
11953
  };
11954
11954
 
11955
- class RNSpeechDetector {
11956
- constructor(externalAudioStream) {
11957
- this.pc1 = new RTCPeerConnection({});
11958
- this.pc2 = new RTCPeerConnection({});
11959
- this.isStopped = false;
11960
- this.externalAudioStream = externalAudioStream;
11961
- }
11962
- /**
11963
- * Starts the speech detection.
11964
- */
11965
- async start(onSoundDetectedStateChanged) {
11966
- let detachListeners;
11967
- let unsubscribe;
11968
- try {
11969
- this.isStopped = false;
11970
- const audioStream = this.externalAudioStream != null
11971
- ? this.externalAudioStream
11972
- : await navigator.mediaDevices.getUserMedia({ audio: true });
11973
- this.audioStream = audioStream;
11974
- const onPc1IceCandidate = (e) => {
11975
- this.forwardIceCandidate(this.pc2, e.candidate);
11976
- };
11977
- const onPc2IceCandidate = (e) => {
11978
- this.forwardIceCandidate(this.pc1, e.candidate);
11979
- };
11980
- const onTrackPc2 = (e) => {
11981
- e.streams[0].getTracks().forEach((track) => {
11982
- // In RN, the remote track is automatically added to the audio output device
11983
- // so we need to mute it to avoid hearing the audio back
11984
- // @ts-expect-error _setVolume is a private method in react-native-webrtc
11985
- track._setVolume(0);
11986
- });
11987
- };
11988
- this.pc1.addEventListener('icecandidate', onPc1IceCandidate);
11989
- this.pc2.addEventListener('icecandidate', onPc2IceCandidate);
11990
- this.pc2.addEventListener('track', onTrackPc2);
11991
- detachListeners = () => {
11992
- this.pc1.removeEventListener('icecandidate', onPc1IceCandidate);
11993
- this.pc2.removeEventListener('icecandidate', onPc2IceCandidate);
11994
- this.pc2.removeEventListener('track', onTrackPc2);
11995
- };
11996
- audioStream
11997
- .getTracks()
11998
- .forEach((track) => this.pc1.addTrack(track, audioStream));
11999
- const offer = await this.pc1.createOffer({});
12000
- await this.pc2.setRemoteDescription(offer);
12001
- await this.pc1.setLocalDescription(offer);
12002
- const answer = await this.pc2.createAnswer();
12003
- await this.pc1.setRemoteDescription(answer);
12004
- await this.pc2.setLocalDescription(answer);
12005
- unsubscribe = this.onSpeakingDetectedStateChange(onSoundDetectedStateChanged);
12006
- return () => {
12007
- detachListeners?.();
12008
- unsubscribe?.();
12009
- this.stop();
12010
- };
12011
- }
12012
- catch (error) {
12013
- detachListeners?.();
12014
- unsubscribe?.();
12015
- this.stop();
12016
- const logger = videoLoggerSystem.getLogger('RNSpeechDetector');
12017
- logger.error('error handling permissions: ', error);
12018
- return () => { };
12019
- }
12020
- }
12021
- /**
12022
- * Stops the speech detection and releases all allocated resources.
12023
- */
12024
- stop() {
12025
- if (this.isStopped)
12026
- return;
12027
- this.isStopped = true;
12028
- this.pc1.close();
12029
- this.pc2.close();
12030
- if (this.externalAudioStream != null) {
12031
- this.externalAudioStream = undefined;
12032
- }
12033
- else {
12034
- this.cleanupAudioStream();
12035
- }
12036
- }
12037
- /**
12038
- * Public method that detects the audio levels and returns the status.
12039
- */
12040
- onSpeakingDetectedStateChange(onSoundDetectedStateChanged) {
12041
- const initialBaselineNoiseLevel = 0.13;
12042
- let baselineNoiseLevel = initialBaselineNoiseLevel;
12043
- let speechDetected = false;
12044
- let speechTimer;
12045
- let silenceTimer;
12046
- const audioLevelHistory = []; // Store recent audio levels for smoother detection
12047
- const historyLength = 10;
12048
- const silenceThreshold = 1.1;
12049
- const resetThreshold = 0.9;
12050
- const speechTimeout = 500; // Speech is set to true after 500ms of audio detection
12051
- const silenceTimeout = 5000; // Reset baseline after 5 seconds of silence
12052
- const checkAudioLevel = async () => {
12053
- try {
12054
- const stats = await this.pc1.getStats();
12055
- const report = flatten(stats);
12056
- // Audio levels are present inside stats of type `media-source` and of kind `audio`
12057
- const audioMediaSourceStats = report.find((stat) => stat.type === 'media-source' &&
12058
- stat.kind === 'audio');
12059
- if (audioMediaSourceStats) {
12060
- const { audioLevel } = audioMediaSourceStats;
12061
- if (audioLevel) {
12062
- // Update audio level history (with max historyLength sized array)
12063
- audioLevelHistory.push(audioLevel);
12064
- if (audioLevelHistory.length > historyLength) {
12065
- audioLevelHistory.shift();
12066
- }
12067
- // Calculate average audio level
12068
- const avgAudioLevel = audioLevelHistory.reduce((a, b) => a + b, 0) /
12069
- audioLevelHistory.length;
12070
- // Update baseline (if necessary) based on silence detection
12071
- if (avgAudioLevel < baselineNoiseLevel * silenceThreshold) {
12072
- if (!silenceTimer) {
12073
- silenceTimer = setTimeout(() => {
12074
- baselineNoiseLevel = Math.min(avgAudioLevel * resetThreshold, initialBaselineNoiseLevel);
12075
- }, silenceTimeout);
12076
- }
12077
- }
12078
- else {
12079
- clearTimeout(silenceTimer);
12080
- silenceTimer = undefined;
12081
- }
12082
- // Speech detection with hysteresis
12083
- if (avgAudioLevel > baselineNoiseLevel * 1.5) {
12084
- if (!speechDetected) {
12085
- speechDetected = true;
12086
- onSoundDetectedStateChanged({
12087
- isSoundDetected: true,
12088
- audioLevel,
12089
- });
12090
- }
12091
- clearTimeout(speechTimer);
12092
- speechTimer = setTimeout(() => {
12093
- speechDetected = false;
12094
- onSoundDetectedStateChanged({
12095
- isSoundDetected: false,
12096
- audioLevel: 0,
12097
- });
12098
- }, speechTimeout);
12099
- }
12100
- }
12101
- }
12102
- }
12103
- catch (error) {
12104
- const logger = videoLoggerSystem.getLogger('RNSpeechDetector');
12105
- logger.error('error checking audio level from stats', error);
12106
- }
12107
- };
12108
- const intervalId = setInterval(checkAudioLevel, 250);
12109
- return () => {
12110
- clearInterval(intervalId);
12111
- clearTimeout(speechTimer);
12112
- clearTimeout(silenceTimer);
12113
- };
12114
- }
12115
- cleanupAudioStream() {
12116
- if (!this.audioStream) {
12117
- return;
12118
- }
12119
- this.audioStream.getTracks().forEach((track) => track.stop());
12120
- if (
12121
- // @ts-expect-error release() is present in react-native-webrtc
12122
- typeof this.audioStream.release === 'function') {
12123
- // @ts-expect-error called to dispose the stream in RN
12124
- this.audioStream.release();
12125
- }
12126
- }
12127
- forwardIceCandidate(destination, candidate) {
12128
- if (this.isStopped ||
12129
- !candidate ||
12130
- destination.signalingState === 'closed') {
12131
- return;
12132
- }
12133
- destination.addIceCandidate(candidate).catch(() => {
12134
- // silently ignore the error
12135
- const logger = videoLoggerSystem.getLogger('RNSpeechDetector');
12136
- logger.info('cannot add ice candidate - ignoring');
12137
- });
12138
- }
12139
- }
12140
-
12141
11955
  class MicrophoneManager extends AudioDeviceManager {
12142
11956
  constructor(call, devicePersistence, disableMode = 'stop-tracks') {
12143
11957
  super(call, new MicrophoneManagerState(disableMode, call.tracer), TrackType.AUDIO, devicePersistence);
@@ -12450,13 +12264,16 @@ class MicrophoneManager extends AudioDeviceManager {
12450
12264
  return;
12451
12265
  await this.teardownSpeakingWhileMutedDetection();
12452
12266
  if (isReactNative()) {
12453
- this.rnSpeechDetector = new RNSpeechDetector();
12454
- const unsubscribe = await this.rnSpeechDetector.start((event) => {
12267
+ const speechActivity = globalThis.streamRNVideoSDK?.nativeEvents?.speechActivity;
12268
+ if (!speechActivity) {
12269
+ this.logger.warn('Native speech activity not available, make sure the "@stream-io/react-native-webrtc" peer dependency version is satisfied');
12270
+ return;
12271
+ }
12272
+ const unsubscribe = speechActivity.subscribe((event) => {
12455
12273
  this.state.setSpeakingWhileMuted(event.isSoundDetected);
12456
12274
  });
12457
12275
  this.soundDetectorCleanup = async () => {
12458
12276
  unsubscribe();
12459
- this.rnSpeechDetector = undefined;
12460
12277
  };
12461
12278
  }
12462
12279
  else {
@@ -16169,7 +15986,7 @@ class StreamClient {
16169
15986
  this.getUserAgent = () => {
16170
15987
  if (!this.cachedUserAgent) {
16171
15988
  const { clientAppIdentifier = {} } = this.options;
16172
- const { sdkName = 'js', sdkVersion = "1.47.0", ...extras } = clientAppIdentifier;
15989
+ const { sdkName = 'js', sdkVersion = "1.48.0", ...extras } = clientAppIdentifier;
16173
15990
  this.cachedUserAgent = [
16174
15991
  `stream-video-${sdkName}-v${sdkVersion}`,
16175
15992
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
@@ -16844,7 +16661,6 @@ exports.MicrophoneManager = MicrophoneManager;
16844
16661
  exports.MicrophoneManagerState = MicrophoneManagerState;
16845
16662
  exports.NoiseCancellationSettingsModeEnum = NoiseCancellationSettingsModeEnum;
16846
16663
  exports.OwnCapability = OwnCapability;
16847
- exports.RNSpeechDetector = RNSpeechDetector;
16848
16664
  exports.RTMPBroadcastRequestQualityEnum = RTMPBroadcastRequestQualityEnum;
16849
16665
  exports.RTMPSettingsRequestQualityEnum = RTMPSettingsRequestQualityEnum;
16850
16666
  exports.RawRecordingSettingsRequestModeEnum = RawRecordingSettingsRequestModeEnum;