@stream-io/video-client 1.4.0 → 1.4.1

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 CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ### [1.4.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.4.0...@stream-io/video-client-1.4.1) (2024-06-19)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * perform full reconnect if ice restart fails ([#1408](https://github.com/GetStream/stream-video-js/issues/1408)) ([641df7e](https://github.com/GetStream/stream-video-js/commit/641df7e50522452171498a9cf3de893472fe7b7b))
11
+
5
12
  ## [1.4.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.3.1...@stream-io/video-client-1.4.0) (2024-06-19)
6
13
 
7
14
 
@@ -8255,8 +8255,9 @@ class Publisher {
8255
8255
  * @param isDtxEnabled whether DTX is enabled.
8256
8256
  * @param isRedEnabled whether RED is enabled.
8257
8257
  * @param iceRestartDelay the delay in milliseconds to wait before restarting ICE once connection goes to `disconnected` state.
8258
+ * @param onUnrecoverableError a callback to call when an unrecoverable error occurs.
8258
8259
  */
8259
- constructor({ connectionConfig, sfuClient, dispatcher, state, isDtxEnabled, isRedEnabled, iceRestartDelay = 2500, }) {
8260
+ constructor({ connectionConfig, sfuClient, dispatcher, state, isDtxEnabled, isRedEnabled, iceRestartDelay = 2500, onUnrecoverableError, }) {
8260
8261
  this.transceiverRegistry = {
8261
8262
  [TrackType.AUDIO]: undefined,
8262
8263
  [TrackType.VIDEO]: undefined,
@@ -8777,15 +8778,16 @@ class Publisher {
8777
8778
  logger$3('debug', `ICE Connection state changed to`, state);
8778
8779
  const hasNetworkConnection = this.state.callingState !== CallingState.OFFLINE;
8779
8780
  if (state === 'failed') {
8780
- logger$3('warn', `Attempting to restart ICE`);
8781
+ logger$3('debug', `Attempting to restart ICE`);
8781
8782
  this.restartIce().catch((e) => {
8782
8783
  logger$3('error', `ICE restart error`, e);
8784
+ this.onUnrecoverableError?.();
8783
8785
  });
8784
8786
  }
8785
8787
  else if (state === 'disconnected' && hasNetworkConnection) {
8786
8788
  // when in `disconnected` state, the browser may recover automatically,
8787
8789
  // hence, we delay the ICE restart
8788
- logger$3('warn', `Scheduling ICE restart in ${this.iceRestartDelay} ms.`);
8790
+ logger$3('debug', `Scheduling ICE restart in ${this.iceRestartDelay} ms.`);
8789
8791
  this.iceRestartTimeout = setTimeout(() => {
8790
8792
  // check if the state is still `disconnected` or `failed`
8791
8793
  // as the connection may have recovered (or failed) in the meantime
@@ -8793,6 +8795,7 @@ class Publisher {
8793
8795
  this.pc.iceConnectionState === 'failed') {
8794
8796
  this.restartIce().catch((e) => {
8795
8797
  logger$3('error', `ICE restart error`, e);
8798
+ this.onUnrecoverableError?.();
8796
8799
  });
8797
8800
  }
8798
8801
  else {
@@ -8820,11 +8823,13 @@ class Publisher {
8820
8823
  this.isDtxEnabled = isDtxEnabled;
8821
8824
  this.isRedEnabled = isRedEnabled;
8822
8825
  this.iceRestartDelay = iceRestartDelay;
8826
+ this.onUnrecoverableError = onUnrecoverableError;
8823
8827
  this.unsubscribeOnIceRestart = dispatcher.on('iceRestart', (iceRestart) => {
8824
8828
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
8825
8829
  return;
8826
8830
  this.restartIce().catch((err) => {
8827
8831
  logger$3('warn', `ICERestart failed`, err);
8832
+ this.onUnrecoverableError?.();
8828
8833
  });
8829
8834
  });
8830
8835
  }
@@ -8854,8 +8859,9 @@ class Subscriber {
8854
8859
  * @param state the state of the call.
8855
8860
  * @param connectionConfig the connection configuration to use.
8856
8861
  * @param iceRestartDelay the delay in milliseconds to wait before restarting ICE when connection goes to `disconnected` state.
8862
+ * @param onUnrecoverableError a callback to call when an unrecoverable error occurs.
8857
8863
  */
8858
- constructor({ sfuClient, dispatcher, state, connectionConfig, iceRestartDelay = 2500, }) {
8864
+ constructor({ sfuClient, dispatcher, state, connectionConfig, iceRestartDelay = 2500, onUnrecoverableError, }) {
8859
8865
  this.isIceRestarting = false;
8860
8866
  /**
8861
8867
  * Creates a new `RTCPeerConnection` instance with the given configuration.
@@ -8965,6 +8971,10 @@ class Subscriber {
8965
8971
  logger$2('debug', 'ICE restart is already in progress');
8966
8972
  return;
8967
8973
  }
8974
+ if (this.pc.connectionState === 'new') {
8975
+ logger$2('debug', `ICE connection is not yet established, skipping restart.`);
8976
+ return;
8977
+ }
8968
8978
  const previousIsIceRestarting = this.isIceRestarting;
8969
8979
  try {
8970
8980
  this.isIceRestarting = true;
@@ -8985,17 +8995,18 @@ class Subscriber {
8985
8995
  const participantToUpdate = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
8986
8996
  logger$2('debug', `[onTrack]: Got remote ${trackType} track for userId: ${participantToUpdate?.userId}`, e.track.id, e.track);
8987
8997
  if (!participantToUpdate) {
8988
- logger$2('error', `[onTrack]: Received track for unknown participant: ${trackId}`, e);
8998
+ logger$2('warn', `[onTrack]: Received track for unknown participant: ${trackId}`, e);
8989
8999
  return;
8990
9000
  }
9001
+ const trackDebugInfo = `${participantToUpdate.userId} ${trackType}:${trackId}`;
8991
9002
  e.track.addEventListener('mute', () => {
8992
- logger$2('info', `[onTrack]: Track muted: ${participantToUpdate.userId} ${trackType}:${trackId}`);
9003
+ logger$2('info', `[onTrack]: Track muted: ${trackDebugInfo}`);
8993
9004
  });
8994
9005
  e.track.addEventListener('unmute', () => {
8995
- logger$2('info', `[onTrack]: Track unmuted: ${participantToUpdate.userId} ${trackType}:${trackId}`);
9006
+ logger$2('info', `[onTrack]: Track unmuted: ${trackDebugInfo}`);
8996
9007
  });
8997
9008
  e.track.addEventListener('ended', () => {
8998
- logger$2('info', `[onTrack]: Track ended: ${participantToUpdate.userId} ${trackType}:${trackId}`);
9009
+ logger$2('info', `[onTrack]: Track ended: ${trackDebugInfo}`);
8999
9010
  });
9000
9011
  const streamKindProp = {
9001
9012
  TRACK_TYPE_AUDIO: 'audioStream',
@@ -9065,15 +9076,16 @@ class Subscriber {
9065
9076
  return;
9066
9077
  const hasNetworkConnection = this.state.callingState !== CallingState.OFFLINE;
9067
9078
  if (state === 'failed') {
9068
- logger$2('warn', `Attempting to restart ICE`);
9079
+ logger$2('debug', `Attempting to restart ICE`);
9069
9080
  this.restartIce().catch((e) => {
9070
9081
  logger$2('error', `ICE restart failed`, e);
9082
+ this.onUnrecoverableError?.();
9071
9083
  });
9072
9084
  }
9073
9085
  else if (state === 'disconnected' && hasNetworkConnection) {
9074
9086
  // when in `disconnected` state, the browser may recover automatically,
9075
9087
  // hence, we delay the ICE restart
9076
- logger$2('warn', `Scheduling ICE restart in ${this.iceRestartDelay} ms.`);
9088
+ logger$2('debug', `Scheduling ICE restart in ${this.iceRestartDelay} ms.`);
9077
9089
  this.iceRestartTimeout = setTimeout(() => {
9078
9090
  // check if the state is still `disconnected` or `failed`
9079
9091
  // as the connection may have recovered (or failed) in the meantime
@@ -9081,12 +9093,13 @@ class Subscriber {
9081
9093
  this.pc.iceConnectionState === 'failed') {
9082
9094
  this.restartIce().catch((e) => {
9083
9095
  logger$2('error', `ICE restart failed`, e);
9096
+ this.onUnrecoverableError?.();
9084
9097
  });
9085
9098
  }
9086
9099
  else {
9087
9100
  logger$2('debug', `Scheduled ICE restart: connection recovered, canceled.`);
9088
9101
  }
9089
- }, 5000);
9102
+ }, this.iceRestartDelay);
9090
9103
  }
9091
9104
  };
9092
9105
  this.onIceGatheringStateChange = () => {
@@ -9102,6 +9115,7 @@ class Subscriber {
9102
9115
  this.sfuClient = sfuClient;
9103
9116
  this.state = state;
9104
9117
  this.iceRestartDelay = iceRestartDelay;
9118
+ this.onUnrecoverableError = onUnrecoverableError;
9105
9119
  this.pc = this.createPeerConnection(connectionConfig);
9106
9120
  this.unregisterOnSubscriberOffer = dispatcher.on('subscriberOffer', (subscriberOffer) => {
9107
9121
  this.negotiate(subscriberOffer).catch((err) => {
@@ -9113,6 +9127,7 @@ class Subscriber {
9113
9127
  return;
9114
9128
  this.restartIce().catch((err) => {
9115
9129
  logger$2('warn', `ICERestart failed`, err);
9130
+ this.onUnrecoverableError?.();
9116
9131
  });
9117
9132
  });
9118
9133
  }
@@ -12795,6 +12810,11 @@ class Call {
12795
12810
  dispatcher: this.dispatcher,
12796
12811
  state: this.state,
12797
12812
  connectionConfig,
12813
+ onUnrecoverableError: () => {
12814
+ reconnect('full', 'unrecoverable subscriber error').catch((err) => {
12815
+ this.logger('debug', '[Rejoin]: Rejoin failed', err);
12816
+ });
12817
+ },
12798
12818
  });
12799
12819
  }
12800
12820
  // anonymous users can't publish anything hence, there is no need
@@ -12811,6 +12831,11 @@ class Call {
12811
12831
  connectionConfig,
12812
12832
  isDtxEnabled,
12813
12833
  isRedEnabled,
12834
+ onUnrecoverableError: () => {
12835
+ reconnect('full', 'unrecoverable publisher error').catch((err) => {
12836
+ this.logger('debug', '[Rejoin]: Rejoin failed', err);
12837
+ });
12838
+ },
12814
12839
  });
12815
12840
  }
12816
12841
  if (!this.statsReporter) {
@@ -15384,7 +15409,7 @@ class StreamClient {
15384
15409
  });
15385
15410
  };
15386
15411
  this.getUserAgent = () => {
15387
- const version = "1.4.0" ;
15412
+ const version = "1.4.1" ;
15388
15413
  return (this.userAgent ||
15389
15414
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
15390
15415
  };