@stream-io/video-client 1.18.5 → 1.18.7

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,21 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [1.18.7](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.18.6...@stream-io/video-client-1.18.7) (2025-03-20)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * rename `toJSON` to `asJSON` ([#1729](https://github.com/GetStream/stream-video-js/issues/1729)) ([0d7d074](https://github.com/GetStream/stream-video-js/commit/0d7d074dac1032690b5f4af4d6ba5fcdd56dfaa2))
11
+ * update call reject reasons ([#1730](https://github.com/GetStream/stream-video-js/issues/1730)) ([100ed6b](https://github.com/GetStream/stream-video-js/commit/100ed6b9323b66e86123917abf4fc2973a677fca))
12
+
13
+ ## [1.18.6](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.18.5...@stream-io/video-client-1.18.6) (2025-03-13)
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * ensure negotiation runs sequentially ([#1722](https://github.com/GetStream/stream-video-js/issues/1722)) ([7e166aa](https://github.com/GetStream/stream-video-js/commit/7e166aaf606c3f751068cf60bd554e6374f701d7))
19
+
5
20
  ## [1.18.5](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.18.4...@stream-io/video-client-1.18.5) (2025-03-12)
6
21
 
7
22
 
@@ -5247,7 +5247,7 @@ class BasePeerConnection {
5247
5247
  this.logger('debug', 'null ice candidate');
5248
5248
  return;
5249
5249
  }
5250
- const iceCandidate = this.toJSON(candidate);
5250
+ const iceCandidate = this.asJSON(candidate);
5251
5251
  this.sfuClient
5252
5252
  .iceTrickle({ peerType: this.peerType, iceCandidate })
5253
5253
  .catch((err) => {
@@ -5259,7 +5259,7 @@ class BasePeerConnection {
5259
5259
  /**
5260
5260
  * Converts the ICE candidate to a JSON string.
5261
5261
  */
5262
- this.toJSON = (candidate) => {
5262
+ this.asJSON = (candidate) => {
5263
5263
  if (!candidate.usernameFragment) {
5264
5264
  // react-native-webrtc doesn't include usernameFragment in the candidate
5265
5265
  const segments = candidate.candidate.split(' ');
@@ -5347,8 +5347,6 @@ class BasePeerConnection {
5347
5347
  this.pc.removeEventListener('icecandidateerror', this.onIceCandidateError);
5348
5348
  this.pc.removeEventListener('signalingstatechange', this.onSignalingChange);
5349
5349
  this.pc.removeEventListener('iceconnectionstatechange', this.onIceConnectionStateChange);
5350
- // cancel any ongoing ICE restart process
5351
- withCancellation('onIceConnectionStateChange', () => Promise.resolve());
5352
5350
  this.pc.removeEventListener('icegatheringstatechange', this.onIceGatherChange);
5353
5351
  this.unsubscribeIceTrickle?.();
5354
5352
  this.subscriptions.forEach((unsubscribe) => unsubscribe());
@@ -5378,12 +5376,6 @@ class TransceiverCache {
5378
5376
  this.get = (publishOption) => {
5379
5377
  return this.findTransceiver(publishOption)?.transceiver;
5380
5378
  };
5381
- /**
5382
- * Gets the last transceiver for the given track type and publish option id.
5383
- */
5384
- this.getWith = (trackType, id) => {
5385
- return this.findTransceiver({ trackType, id })?.transceiver;
5386
- };
5387
5379
  /**
5388
5380
  * Checks if the cache has the given publish option.
5389
5381
  */
@@ -5697,7 +5689,7 @@ class Publisher extends BasePeerConnection {
5697
5689
  const trackToPublish = this.cloneTrack(track);
5698
5690
  const transceiver = this.transceiverCache.get(publishOption);
5699
5691
  if (!transceiver) {
5700
- this.addTransceiver(trackToPublish, publishOption);
5692
+ await this.addTransceiver(trackToPublish, publishOption);
5701
5693
  }
5702
5694
  else {
5703
5695
  const previousTrack = transceiver.sender.track;
@@ -5711,7 +5703,7 @@ class Publisher extends BasePeerConnection {
5711
5703
  /**
5712
5704
  * Adds a new transceiver carrying the given track to the peer connection.
5713
5705
  */
5714
- this.addTransceiver = (track, publishOption) => {
5706
+ this.addTransceiver = async (track, publishOption) => {
5715
5707
  const videoEncodings = computeVideoLayers(track, publishOption);
5716
5708
  const sendEncodings = isSvcCodec(publishOption.codec?.name)
5717
5709
  ? toSvcEncodings(videoEncodings)
@@ -5723,6 +5715,7 @@ class Publisher extends BasePeerConnection {
5723
5715
  const trackType = publishOption.trackType;
5724
5716
  this.logger('debug', `Added ${TrackType[trackType]} transceiver`);
5725
5717
  this.transceiverCache.add(publishOption, transceiver);
5718
+ await this.negotiate();
5726
5719
  };
5727
5720
  /**
5728
5721
  * Synchronizes the current Publisher state with the provided publish options.
@@ -5742,7 +5735,7 @@ class Publisher extends BasePeerConnection {
5742
5735
  // take the track from the existing transceiver for the same track type,
5743
5736
  // clone it and publish it with the new publish options
5744
5737
  const track = this.cloneTrack(item.transceiver.sender.track);
5745
- this.addTransceiver(track, publishOption);
5738
+ await this.addTransceiver(track, publishOption);
5746
5739
  }
5747
5740
  // stop publishing with options not required anymore -> [vp9]
5748
5741
  for (const item of this.transceiverCache.items()) {
@@ -5812,7 +5805,9 @@ class Publisher extends BasePeerConnection {
5812
5805
  const enabledLayers = layers.filter((l) => l.active);
5813
5806
  const tag = 'Update publish quality:';
5814
5807
  this.logger('info', `${tag} requested layers by SFU:`, enabledLayers);
5815
- const sender = this.transceiverCache.getWith(trackType, publishOptionId)?.sender;
5808
+ const transceiverId = this.transceiverCache.find((t) => t.publishOption.id === publishOptionId &&
5809
+ t.publishOption.trackType === trackType);
5810
+ const sender = transceiverId?.transceiver.sender;
5816
5811
  if (!sender) {
5817
5812
  return this.logger('warn', `${tag} no video sender found.`);
5818
5813
  }
@@ -5820,8 +5815,8 @@ class Publisher extends BasePeerConnection {
5820
5815
  if (params.encodings.length === 0) {
5821
5816
  return this.logger('warn', `${tag} there are no encodings set.`);
5822
5817
  }
5823
- const [codecInUse] = params.codecs;
5824
- const usesSvcCodec = codecInUse && isSvcCodec(codecInUse.mimeType);
5818
+ const codecInUse = transceiverId?.publishOption.codec?.name;
5819
+ const usesSvcCodec = codecInUse && isSvcCodec(codecInUse);
5825
5820
  let changed = false;
5826
5821
  for (const encoder of params.encodings) {
5827
5822
  const layer = usesSvcCodec
@@ -5879,40 +5874,32 @@ class Publisher extends BasePeerConnection {
5879
5874
  }
5880
5875
  await this.negotiate({ iceRestart: true });
5881
5876
  };
5882
- this.onNegotiationNeeded = () => {
5883
- withCancellation('publisher.negotiate', (signal) => this.negotiate().catch((err) => {
5884
- if (signal.aborted)
5885
- return;
5886
- this.logger('error', `Negotiation failed.`, err);
5887
- this.onUnrecoverableError?.();
5888
- }));
5889
- };
5890
5877
  /**
5891
5878
  * Initiates a new offer/answer exchange with the currently connected SFU.
5892
5879
  *
5893
5880
  * @param options the optional offer options to use.
5894
5881
  */
5895
5882
  this.negotiate = async (options) => {
5896
- const offer = await this.pc.createOffer(options);
5897
- const trackInfos = this.getAnnouncedTracks(offer.sdp);
5898
- if (trackInfos.length === 0) {
5899
- throw new Error(`Can't negotiate without announcing any tracks`);
5900
- }
5901
- try {
5902
- this.isIceRestarting = options?.iceRestart ?? false;
5903
- await this.pc.setLocalDescription(offer);
5904
- const { response } = await this.sfuClient.setPublisher({
5905
- sdp: offer.sdp || '',
5906
- tracks: trackInfos,
5907
- });
5908
- if (response.error)
5909
- throw new Error(response.error.message);
5910
- await this.pc.setRemoteDescription({ type: 'answer', sdp: response.sdp });
5911
- }
5912
- finally {
5913
- this.isIceRestarting = false;
5914
- }
5915
- this.addTrickledIceCandidates();
5883
+ return withoutConcurrency('publisher.negotiate', async () => {
5884
+ const offer = await this.pc.createOffer(options);
5885
+ const tracks = this.getAnnouncedTracks(offer.sdp);
5886
+ if (!tracks.length)
5887
+ throw new Error(`Can't negotiate without any tracks`);
5888
+ try {
5889
+ this.isIceRestarting = options?.iceRestart ?? false;
5890
+ await this.pc.setLocalDescription(offer);
5891
+ const { sdp = '' } = offer;
5892
+ const { response } = await this.sfuClient.setPublisher({ sdp, tracks });
5893
+ if (response.error)
5894
+ throw new Error(response.error.message);
5895
+ const { sdp: answerSdp } = response;
5896
+ await this.pc.setRemoteDescription({ type: 'answer', sdp: answerSdp });
5897
+ }
5898
+ finally {
5899
+ this.isIceRestarting = false;
5900
+ }
5901
+ this.addTrickledIceCandidates();
5902
+ });
5916
5903
  };
5917
5904
  /**
5918
5905
  * Returns a list of tracks that are currently being published.
@@ -5996,7 +5983,6 @@ class Publisher extends BasePeerConnection {
5996
5983
  this.clonedTracks.delete(track);
5997
5984
  };
5998
5985
  this.publishOptions = publishOptions;
5999
- this.pc.addEventListener('negotiationneeded', this.onNegotiationNeeded);
6000
5986
  this.on('iceRestart', (iceRestart) => {
6001
5987
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
6002
5988
  return;
@@ -6015,17 +6001,6 @@ class Publisher extends BasePeerConnection {
6015
6001
  return this.syncPublishOptions();
6016
6002
  });
6017
6003
  }
6018
- /**
6019
- * Detaches the event handlers from the `RTCPeerConnection`.
6020
- * This is useful when we want to replace the `RTCPeerConnection`
6021
- * instance with a new one (in case of migration).
6022
- */
6023
- detachEventHandlers() {
6024
- super.detachEventHandlers();
6025
- this.pc.removeEventListener('negotiationneeded', this.onNegotiationNeeded);
6026
- // abort any ongoing negotiation
6027
- withCancellation('publisher.negotiate', () => Promise.resolve());
6028
- }
6029
6004
  /**
6030
6005
  * Disposes this Publisher instance.
6031
6006
  */
@@ -7457,7 +7432,7 @@ const aggregate = (stats) => {
7457
7432
  return report;
7458
7433
  };
7459
7434
 
7460
- const version = "1.18.5";
7435
+ const version = "1.18.7";
7461
7436
  const [major, minor, patch] = version.split('.');
7462
7437
  let sdkInfo = {
7463
7438
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -10428,7 +10403,7 @@ class Call {
10428
10403
  }
10429
10404
  if (callingState === CallingState.RINGING && reject !== false) {
10430
10405
  if (reject) {
10431
- await this.reject(reason);
10406
+ await this.reject('decline');
10432
10407
  }
10433
10408
  else {
10434
10409
  // if reject was undefined, we still have to cancel the call automatically
@@ -10458,6 +10433,7 @@ class Call {
10458
10433
  this.initialized = false;
10459
10434
  this.hasJoinedOnce = false;
10460
10435
  this.ringingSubject.next(false);
10436
+ this.cancelAutoDrop();
10461
10437
  this.clientStore.unregisterCall(this);
10462
10438
  this.camera.dispose();
10463
10439
  this.microphone.dispose();
@@ -10596,7 +10572,7 @@ class Call {
10596
10572
  *
10597
10573
  * @param reason the reason for rejecting the call.
10598
10574
  */
10599
- this.reject = async (reason) => {
10575
+ this.reject = async (reason = 'decline') => {
10600
10576
  return this.streamClient.post(`${this.streamClientBasePath}/reject`, { reason: reason });
10601
10577
  };
10602
10578
  /**
@@ -13096,7 +13072,7 @@ class StreamClient {
13096
13072
  this.getUserAgent = () => {
13097
13073
  if (!this.cachedUserAgent) {
13098
13074
  const { clientAppIdentifier = {} } = this.options;
13099
- const { sdkName = 'js', sdkVersion = "1.18.5", ...extras } = clientAppIdentifier;
13075
+ const { sdkName = 'js', sdkVersion = "1.18.7", ...extras } = clientAppIdentifier;
13100
13076
  this.cachedUserAgent = [
13101
13077
  `stream-video-${sdkName}-v${sdkVersion}`,
13102
13078
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),