@stream-io/video-client 1.54.0 → 1.54.1-beta.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
@@ -527,6 +527,7 @@ class ErrorFromResponse extends Error {
527
527
  }
528
528
  }
529
529
 
530
+ /* eslint-disable */
530
531
  // @generated by protobuf-ts 2.10.0 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
531
532
  // @generated from protobuf file "google/protobuf/struct.proto" (package "google.protobuf", syntax proto3)
532
533
  // tslint:disable
@@ -792,6 +793,7 @@ class ListValue$Type extends runtime.MessageType {
792
793
  */
793
794
  const ListValue = new ListValue$Type();
794
795
 
796
+ /* eslint-disable */
795
797
  // @generated by protobuf-ts 2.10.0 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
796
798
  // @generated from protobuf file "google/protobuf/timestamp.proto" (package "google.protobuf", syntax proto3)
797
799
  // tslint:disable
@@ -1858,6 +1860,12 @@ class TrackInfo$Type extends runtime.MessageType {
1858
1860
  kind: 'scalar',
1859
1861
  T: 5 /*ScalarType.INT32*/,
1860
1862
  },
1863
+ {
1864
+ no: 13,
1865
+ name: 'self_sub_audio_video',
1866
+ kind: 'scalar',
1867
+ T: 8 /*ScalarType.BOOL*/,
1868
+ },
1861
1869
  ]);
1862
1870
  }
1863
1871
  }
@@ -6660,7 +6668,7 @@ const getSdkVersion = (sdk) => {
6660
6668
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6661
6669
  };
6662
6670
 
6663
- const version = "1.54.0";
6671
+ const version = "1.54.1-beta.0";
6664
6672
  const [major, minor, patch] = version.split('.');
6665
6673
  let sdkInfo = {
6666
6674
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -6804,7 +6812,7 @@ const getClientDetails = async () => {
6804
6812
  .join(' '),
6805
6813
  version: '',
6806
6814
  },
6807
- webrtcVersion: browserVersion,
6815
+ webrtcVersion: webRtcInfo?.version || '',
6808
6816
  };
6809
6817
  };
6810
6818
 
@@ -8448,7 +8456,7 @@ class Publisher extends BasePeerConnection {
8448
8456
  /**
8449
8457
  * Constructs a new `Publisher` instance.
8450
8458
  */
8451
- constructor(baseOptions, publishOptions) {
8459
+ constructor(baseOptions, publishOptions, opts = {}) {
8452
8460
  super(PeerType.PUBLISHER_UNSPECIFIED, baseOptions);
8453
8461
  this.transceiverCache = new TransceiverCache();
8454
8462
  this.clonedTracks = new Set();
@@ -8869,6 +8877,7 @@ class Publisher extends BasePeerConnection {
8869
8877
  muted: !isTrackLive,
8870
8878
  codec: publishOption.codec,
8871
8879
  publishOptionId: publishOption.id,
8880
+ selfSubAudioVideo: this.selfSubEnabled,
8872
8881
  };
8873
8882
  };
8874
8883
  this.cloneTrack = (track) => {
@@ -8949,6 +8958,7 @@ class Publisher extends BasePeerConnection {
8949
8958
  });
8950
8959
  };
8951
8960
  this.publishOptions = publishOptions;
8961
+ this.selfSubEnabled = opts.selfSubEnabled ?? false;
8952
8962
  this.on('iceRestart', (iceRestart) => {
8953
8963
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
8954
8964
  return;
@@ -9030,6 +9040,13 @@ class Subscriber extends BasePeerConnection {
9030
9040
  */
9031
9041
  constructor(opts) {
9032
9042
  super(PeerType.SUBSCRIBER, opts);
9043
+ /**
9044
+ * Remote streams received from the SFU. For a self-sub case
9045
+ * we need to be able to distinguish between the local capture stream.
9046
+ * The map will never contain local streams so we can safely use it to
9047
+ * check if the stream is remote and dispose it when needed.
9048
+ */
9049
+ this.trackedStreams = new WeakSet();
9033
9050
  /**
9034
9051
  * Restarts the ICE connection and renegotiates with the SFU.
9035
9052
  */
@@ -9064,6 +9081,7 @@ class Subscriber extends BasePeerConnection {
9064
9081
  // example: `e3f6aaf8-b03d-4911-be36-83f47d37a76a:TRACK_TYPE_VIDEO`
9065
9082
  const [trackId, rawTrackType] = primaryStream.id.split(':');
9066
9083
  const participantToUpdate = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
9084
+ const isSelfSub = !!participantToUpdate?.isLocalParticipant;
9067
9085
  this.logger.debug(`[onTrack]: Got remote ${rawTrackType} track for userId: ${participantToUpdate?.userId}`, track.id, track);
9068
9086
  const trackType = toTrackType(rawTrackType);
9069
9087
  if (!trackType) {
@@ -9088,6 +9106,9 @@ class Subscriber extends BasePeerConnection {
9088
9106
  this.setRemoteTrackInterrupted(trackId, trackType, true);
9089
9107
  }
9090
9108
  this.trackIdToTrackType.set(track.id, trackType);
9109
+ if (isSelfSub) {
9110
+ this.trackedStreams.add(primaryStream);
9111
+ }
9091
9112
  if (!participantToUpdate) {
9092
9113
  this.logger.warn(`[onTrack]: Received track for unknown participant: ${trackId}`, e);
9093
9114
  this.state.registerOrphanedTrack({
@@ -9103,6 +9124,12 @@ class Subscriber extends BasePeerConnection {
9103
9124
  this.logger.error(`Unknown track type: ${rawTrackType}`);
9104
9125
  return;
9105
9126
  }
9127
+ // Self-sub loopback audio routes to the speaker by default, which
9128
+ // would echo the local user's voice. Default-mute here; consumers
9129
+ // (the loopback recording hook) re-enable explicitly when needed.
9130
+ if (isSelfSub && e.track.kind === 'audio') {
9131
+ e.track.enabled = false;
9132
+ }
9106
9133
  // get the previous stream to dispose it later
9107
9134
  // usually this happens during migration, when the stream is replaced
9108
9135
  // with a new one but the old one is still in the state
@@ -9111,8 +9138,12 @@ class Subscriber extends BasePeerConnection {
9111
9138
  this.state.updateParticipant(participantToUpdate.sessionId, {
9112
9139
  [streamKindProp]: primaryStream,
9113
9140
  });
9114
- // now, dispose the previous stream if it exists
9115
9141
  if (previousStream) {
9142
+ if (isSelfSub && !this.trackedStreams.has(previousStream)) {
9143
+ // this is the local capture stream, we don't want to dispose it
9144
+ this.logger.debug(`[onTrack]: Skipping cleanup of previous ${e.track.kind} stream for userId: ${participantToUpdate.userId} because it is not tracked`);
9145
+ return;
9146
+ }
9116
9147
  this.logger.info(`[onTrack]: Cleaning up previous remote ${track.kind} tracks for userId: ${participantToUpdate.userId}`);
9117
9148
  previousStream.getTracks().forEach((t) => {
9118
9149
  t.stop();
@@ -13935,6 +13966,7 @@ class Call {
13935
13966
  // maintain the order of publishing tracks to restore them after a reconnection
13936
13967
  // it shouldn't contain duplicates
13937
13968
  this.trackPublishOrder = [];
13969
+ this.selfSubEnabled = false;
13938
13970
  this.hasJoinedOnce = false;
13939
13971
  this.deviceSettingsAppliedOnce = false;
13940
13972
  this.initialized = false;
@@ -14284,6 +14316,30 @@ class Call {
14284
14316
  await Promise.all(stopOnLeavePromises);
14285
14317
  });
14286
14318
  };
14319
+ /**
14320
+ * The largest video publish dimension across the current publish options.
14321
+ *
14322
+ * @internal
14323
+ */
14324
+ this.getMaxVideoPublishDimension = () => {
14325
+ if (!this.currentPublishOptions)
14326
+ return undefined;
14327
+ let maxDimension;
14328
+ let maxArea = 0;
14329
+ for (const opt of this.currentPublishOptions) {
14330
+ if (opt.trackType !== TrackType.VIDEO)
14331
+ continue;
14332
+ const dim = opt.videoDimension;
14333
+ if (!dim || !dim.width || !dim.height)
14334
+ continue;
14335
+ const area = dim.width * dim.height;
14336
+ if (area > maxArea) {
14337
+ maxDimension = dim;
14338
+ maxArea = area;
14339
+ }
14340
+ }
14341
+ return maxDimension;
14342
+ };
14287
14343
  /**
14288
14344
  * Update from the call response from the "call.ring" event
14289
14345
  * @internal
@@ -14430,7 +14486,7 @@ class Call {
14430
14486
  *
14431
14487
  * @returns a promise which resolves once the call join-flow has finished.
14432
14488
  */
14433
- this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, ...data } = {}) => {
14489
+ this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, selfSubEnabled = false, ...data } = {}) => {
14434
14490
  const callingState = this.state.callingState;
14435
14491
  if ([exports.CallingState.JOINED, exports.CallingState.JOINING].includes(callingState)) {
14436
14492
  throw new Error(`Illegal State: call.join() shall be called only once`);
@@ -14438,6 +14494,9 @@ class Call {
14438
14494
  if (data?.ring) {
14439
14495
  this.ringingSubject.next(true);
14440
14496
  }
14497
+ // we need this to be set before the callingx.joinCall() is
14498
+ // called to avoid registering the test call in the CallKit/Telecom
14499
+ this.selfSubEnabled = selfSubEnabled;
14441
14500
  const callingX = globalThis.streamRNVideoSDK?.callingX;
14442
14501
  if (callingX) {
14443
14502
  // for Android/iOS, we need to start the call in the callingx library as soon as possible
@@ -14827,7 +14886,9 @@ class Call {
14827
14886
  if (closePreviousInstances && this.publisher) {
14828
14887
  await this.publisher.dispose();
14829
14888
  }
14830
- this.publisher = new Publisher(basePeerConnectionOptions, publishOptions);
14889
+ this.publisher = new Publisher(basePeerConnectionOptions, publishOptions, {
14890
+ selfSubEnabled: this.selfSubEnabled,
14891
+ });
14831
14892
  }
14832
14893
  this.statsReporter?.stop();
14833
14894
  if (this.statsReportingIntervalInMs > 0) {
@@ -16323,6 +16384,12 @@ class Call {
16323
16384
  get currentUserId() {
16324
16385
  return this.clientStore.connectedUser?.id;
16325
16386
  }
16387
+ /**
16388
+ * A flag indicating whether self-subscription is enabled for the call.
16389
+ */
16390
+ get isSelfSubEnabled() {
16391
+ return this.selfSubEnabled;
16392
+ }
16326
16393
  /**
16327
16394
  * A flag indicating whether the call was created by the current user.
16328
16395
  */
@@ -17510,11 +17577,11 @@ class StreamClient {
17510
17577
  return await this.wsConnection.connect(this.defaultWSTimeout);
17511
17578
  };
17512
17579
  this.getSdkVersion = () => this.options.clientAppIdentifier?.sdkVersion ||
17513
- "1.54.0";
17580
+ "1.54.1-beta.0";
17514
17581
  this.getUserAgent = () => {
17515
17582
  if (!this.cachedUserAgent) {
17516
17583
  const { clientAppIdentifier = {} } = this.options;
17517
- const { sdkName = 'js', sdkVersion = "1.54.0", ...extras } = clientAppIdentifier;
17584
+ const { sdkName = 'js', sdkVersion = "1.54.1-beta.0", ...extras } = clientAppIdentifier;
17518
17585
  this.cachedUserAgent = [
17519
17586
  `stream-video-${sdkName}-v${sdkVersion}`,
17520
17587
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),