@stream-io/video-client 1.46.0 → 1.47.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.46.0";
6307
+ const version = "1.47.0";
6308
6308
  const [major, minor, patch] = version.split('.');
6309
6309
  let sdkInfo = {
6310
6310
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -10885,8 +10885,14 @@ class DeviceManager {
10885
10885
  this.handleDisconnectedOrReplacedDevices();
10886
10886
  }
10887
10887
  if (this.devicePersistence.enabled) {
10888
- this.subscriptions.push(createSubscription(rxjs.combineLatest([this.state.selectedDevice$, this.state.status$]), ([selectedDevice, status]) => {
10889
- if (!status)
10888
+ this.subscriptions.push(createSubscription(rxjs.combineLatest([
10889
+ this.state.selectedDevice$,
10890
+ this.state.status$,
10891
+ this.state.browserPermissionState$,
10892
+ ]), ([selectedDevice, status, browserPermissionState]) => {
10893
+ if (!status ||
10894
+ (this.isTrackStoppedDueToTrackEnd && status === 'disabled') ||
10895
+ browserPermissionState !== 'granted')
10890
10896
  return;
10891
10897
  this.persistPreference(selectedDevice, status);
10892
10898
  }));
@@ -11651,7 +11657,10 @@ class CameraManager extends DeviceManager {
11651
11657
  const shouldApplyDefaults = this.state.status === undefined &&
11652
11658
  this.state.optimisticStatus === undefined;
11653
11659
  let persistedPreferencesApplied = false;
11654
- if (shouldApplyDefaults && this.devicePersistence.enabled) {
11660
+ const permissionState = await rxjs.firstValueFrom(this.state.browserPermissionState$);
11661
+ if (shouldApplyDefaults &&
11662
+ this.devicePersistence.enabled &&
11663
+ permissionState === 'granted') {
11655
11664
  persistedPreferencesApplied =
11656
11665
  await this.applyPersistedPreferences(enabledInCallType);
11657
11666
  }
@@ -11947,35 +11956,43 @@ class RNSpeechDetector {
11947
11956
  constructor(externalAudioStream) {
11948
11957
  this.pc1 = new RTCPeerConnection({});
11949
11958
  this.pc2 = new RTCPeerConnection({});
11959
+ this.isStopped = false;
11950
11960
  this.externalAudioStream = externalAudioStream;
11951
11961
  }
11952
11962
  /**
11953
11963
  * Starts the speech detection.
11954
11964
  */
11955
11965
  async start(onSoundDetectedStateChanged) {
11966
+ let detachListeners;
11967
+ let unsubscribe;
11956
11968
  try {
11969
+ this.isStopped = false;
11957
11970
  const audioStream = this.externalAudioStream != null
11958
11971
  ? this.externalAudioStream
11959
11972
  : await navigator.mediaDevices.getUserMedia({ audio: true });
11960
11973
  this.audioStream = audioStream;
11961
- this.pc1.addEventListener('icecandidate', (e) => {
11962
- this.pc2.addIceCandidate(e.candidate).catch(() => {
11963
- // do nothing
11964
- });
11965
- });
11966
- this.pc2.addEventListener('icecandidate', async (e) => {
11967
- this.pc1.addIceCandidate(e.candidate).catch(() => {
11968
- // do nothing
11969
- });
11970
- });
11971
- this.pc2.addEventListener('track', (e) => {
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) => {
11972
11981
  e.streams[0].getTracks().forEach((track) => {
11973
11982
  // In RN, the remote track is automatically added to the audio output device
11974
11983
  // so we need to mute it to avoid hearing the audio back
11975
11984
  // @ts-expect-error _setVolume is a private method in react-native-webrtc
11976
11985
  track._setVolume(0);
11977
11986
  });
11978
- });
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
+ };
11979
11996
  audioStream
11980
11997
  .getTracks()
11981
11998
  .forEach((track) => this.pc1.addTrack(track, audioStream));
@@ -11985,13 +12002,17 @@ class RNSpeechDetector {
11985
12002
  const answer = await this.pc2.createAnswer();
11986
12003
  await this.pc1.setRemoteDescription(answer);
11987
12004
  await this.pc2.setLocalDescription(answer);
11988
- const unsubscribe = this.onSpeakingDetectedStateChange(onSoundDetectedStateChanged);
12005
+ unsubscribe = this.onSpeakingDetectedStateChange(onSoundDetectedStateChanged);
11989
12006
  return () => {
11990
- unsubscribe();
12007
+ detachListeners?.();
12008
+ unsubscribe?.();
11991
12009
  this.stop();
11992
12010
  };
11993
12011
  }
11994
12012
  catch (error) {
12013
+ detachListeners?.();
12014
+ unsubscribe?.();
12015
+ this.stop();
11995
12016
  const logger = videoLoggerSystem.getLogger('RNSpeechDetector');
11996
12017
  logger.error('error handling permissions: ', error);
11997
12018
  return () => { };
@@ -12001,6 +12022,9 @@ class RNSpeechDetector {
12001
12022
  * Stops the speech detection and releases all allocated resources.
12002
12023
  */
12003
12024
  stop() {
12025
+ if (this.isStopped)
12026
+ return;
12027
+ this.isStopped = true;
12004
12028
  this.pc1.close();
12005
12029
  this.pc2.close();
12006
12030
  if (this.externalAudioStream != null) {
@@ -12100,6 +12124,18 @@ class RNSpeechDetector {
12100
12124
  this.audioStream.release();
12101
12125
  }
12102
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
+ }
12103
12139
  }
12104
12140
 
12105
12141
  class MicrophoneManager extends AudioDeviceManager {
@@ -12366,7 +12402,10 @@ class MicrophoneManager extends AudioDeviceManager {
12366
12402
  const shouldApplyDefaults = this.state.status === undefined &&
12367
12403
  this.state.optimisticStatus === undefined;
12368
12404
  let persistedPreferencesApplied = false;
12369
- if (shouldApplyDefaults && this.devicePersistence.enabled) {
12405
+ const permissionState = await rxjs.firstValueFrom(this.state.browserPermissionState$);
12406
+ if (shouldApplyDefaults &&
12407
+ this.devicePersistence.enabled &&
12408
+ permissionState === 'granted') {
12370
12409
  persistedPreferencesApplied = await this.applyPersistedPreferences(true);
12371
12410
  }
12372
12411
  const canPublish = this.call.permissionsContext.canPublish(this.trackType);
@@ -12750,7 +12789,12 @@ class SpeakerManager {
12750
12789
  }));
12751
12790
  }
12752
12791
  if (!isReactNative() && this.devicePersistence.enabled) {
12753
- this.subscriptions.push(createSubscription(this.state.selectedDevice$, (selectedDevice) => {
12792
+ this.subscriptions.push(createSubscription(rxjs.combineLatest([
12793
+ this.state.selectedDevice$,
12794
+ getAudioBrowserPermission(this.call.tracer).asStateObservable(),
12795
+ ]), ([selectedDevice, browserPermissionState]) => {
12796
+ if (!selectedDevice || browserPermissionState !== 'granted')
12797
+ return;
12754
12798
  this.persistSpeakerDevicePreference(selectedDevice);
12755
12799
  }));
12756
12800
  }
@@ -16125,7 +16169,7 @@ class StreamClient {
16125
16169
  this.getUserAgent = () => {
16126
16170
  if (!this.cachedUserAgent) {
16127
16171
  const { clientAppIdentifier = {} } = this.options;
16128
- const { sdkName = 'js', sdkVersion = "1.46.0", ...extras } = clientAppIdentifier;
16172
+ const { sdkName = 'js', sdkVersion = "1.47.0", ...extras } = clientAppIdentifier;
16129
16173
  this.cachedUserAgent = [
16130
16174
  `stream-video-${sdkName}-v${sdkVersion}`,
16131
16175
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),