@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.es.js CHANGED
@@ -508,6 +508,7 @@ class ErrorFromResponse extends Error {
508
508
  }
509
509
  }
510
510
 
511
+ /* eslint-disable */
511
512
  // @generated by protobuf-ts 2.10.0 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
512
513
  // @generated from protobuf file "google/protobuf/struct.proto" (package "google.protobuf", syntax proto3)
513
514
  // tslint:disable
@@ -773,6 +774,7 @@ class ListValue$Type extends MessageType {
773
774
  */
774
775
  const ListValue = new ListValue$Type();
775
776
 
777
+ /* eslint-disable */
776
778
  // @generated by protobuf-ts 2.10.0 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
777
779
  // @generated from protobuf file "google/protobuf/timestamp.proto" (package "google.protobuf", syntax proto3)
778
780
  // tslint:disable
@@ -1839,6 +1841,12 @@ class TrackInfo$Type extends MessageType {
1839
1841
  kind: 'scalar',
1840
1842
  T: 5 /*ScalarType.INT32*/,
1841
1843
  },
1844
+ {
1845
+ no: 13,
1846
+ name: 'self_sub_audio_video',
1847
+ kind: 'scalar',
1848
+ T: 8 /*ScalarType.BOOL*/,
1849
+ },
1842
1850
  ]);
1843
1851
  }
1844
1852
  }
@@ -6641,7 +6649,7 @@ const getSdkVersion = (sdk) => {
6641
6649
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6642
6650
  };
6643
6651
 
6644
- const version = "1.54.0";
6652
+ const version = "1.54.1-beta.0";
6645
6653
  const [major, minor, patch] = version.split('.');
6646
6654
  let sdkInfo = {
6647
6655
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -6785,7 +6793,7 @@ const getClientDetails = async () => {
6785
6793
  .join(' '),
6786
6794
  version: '',
6787
6795
  },
6788
- webrtcVersion: browserVersion,
6796
+ webrtcVersion: webRtcInfo?.version || '',
6789
6797
  };
6790
6798
  };
6791
6799
 
@@ -8429,7 +8437,7 @@ class Publisher extends BasePeerConnection {
8429
8437
  /**
8430
8438
  * Constructs a new `Publisher` instance.
8431
8439
  */
8432
- constructor(baseOptions, publishOptions) {
8440
+ constructor(baseOptions, publishOptions, opts = {}) {
8433
8441
  super(PeerType.PUBLISHER_UNSPECIFIED, baseOptions);
8434
8442
  this.transceiverCache = new TransceiverCache();
8435
8443
  this.clonedTracks = new Set();
@@ -8850,6 +8858,7 @@ class Publisher extends BasePeerConnection {
8850
8858
  muted: !isTrackLive,
8851
8859
  codec: publishOption.codec,
8852
8860
  publishOptionId: publishOption.id,
8861
+ selfSubAudioVideo: this.selfSubEnabled,
8853
8862
  };
8854
8863
  };
8855
8864
  this.cloneTrack = (track) => {
@@ -8930,6 +8939,7 @@ class Publisher extends BasePeerConnection {
8930
8939
  });
8931
8940
  };
8932
8941
  this.publishOptions = publishOptions;
8942
+ this.selfSubEnabled = opts.selfSubEnabled ?? false;
8933
8943
  this.on('iceRestart', (iceRestart) => {
8934
8944
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
8935
8945
  return;
@@ -9011,6 +9021,13 @@ class Subscriber extends BasePeerConnection {
9011
9021
  */
9012
9022
  constructor(opts) {
9013
9023
  super(PeerType.SUBSCRIBER, opts);
9024
+ /**
9025
+ * Remote streams received from the SFU. For a self-sub case
9026
+ * we need to be able to distinguish between the local capture stream.
9027
+ * The map will never contain local streams so we can safely use it to
9028
+ * check if the stream is remote and dispose it when needed.
9029
+ */
9030
+ this.trackedStreams = new WeakSet();
9014
9031
  /**
9015
9032
  * Restarts the ICE connection and renegotiates with the SFU.
9016
9033
  */
@@ -9045,6 +9062,7 @@ class Subscriber extends BasePeerConnection {
9045
9062
  // example: `e3f6aaf8-b03d-4911-be36-83f47d37a76a:TRACK_TYPE_VIDEO`
9046
9063
  const [trackId, rawTrackType] = primaryStream.id.split(':');
9047
9064
  const participantToUpdate = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
9065
+ const isSelfSub = !!participantToUpdate?.isLocalParticipant;
9048
9066
  this.logger.debug(`[onTrack]: Got remote ${rawTrackType} track for userId: ${participantToUpdate?.userId}`, track.id, track);
9049
9067
  const trackType = toTrackType(rawTrackType);
9050
9068
  if (!trackType) {
@@ -9069,6 +9087,9 @@ class Subscriber extends BasePeerConnection {
9069
9087
  this.setRemoteTrackInterrupted(trackId, trackType, true);
9070
9088
  }
9071
9089
  this.trackIdToTrackType.set(track.id, trackType);
9090
+ if (isSelfSub) {
9091
+ this.trackedStreams.add(primaryStream);
9092
+ }
9072
9093
  if (!participantToUpdate) {
9073
9094
  this.logger.warn(`[onTrack]: Received track for unknown participant: ${trackId}`, e);
9074
9095
  this.state.registerOrphanedTrack({
@@ -9084,6 +9105,12 @@ class Subscriber extends BasePeerConnection {
9084
9105
  this.logger.error(`Unknown track type: ${rawTrackType}`);
9085
9106
  return;
9086
9107
  }
9108
+ // Self-sub loopback audio routes to the speaker by default, which
9109
+ // would echo the local user's voice. Default-mute here; consumers
9110
+ // (the loopback recording hook) re-enable explicitly when needed.
9111
+ if (isSelfSub && e.track.kind === 'audio') {
9112
+ e.track.enabled = false;
9113
+ }
9087
9114
  // get the previous stream to dispose it later
9088
9115
  // usually this happens during migration, when the stream is replaced
9089
9116
  // with a new one but the old one is still in the state
@@ -9092,8 +9119,12 @@ class Subscriber extends BasePeerConnection {
9092
9119
  this.state.updateParticipant(participantToUpdate.sessionId, {
9093
9120
  [streamKindProp]: primaryStream,
9094
9121
  });
9095
- // now, dispose the previous stream if it exists
9096
9122
  if (previousStream) {
9123
+ if (isSelfSub && !this.trackedStreams.has(previousStream)) {
9124
+ // this is the local capture stream, we don't want to dispose it
9125
+ this.logger.debug(`[onTrack]: Skipping cleanup of previous ${e.track.kind} stream for userId: ${participantToUpdate.userId} because it is not tracked`);
9126
+ return;
9127
+ }
9097
9128
  this.logger.info(`[onTrack]: Cleaning up previous remote ${track.kind} tracks for userId: ${participantToUpdate.userId}`);
9098
9129
  previousStream.getTracks().forEach((t) => {
9099
9130
  t.stop();
@@ -13916,6 +13947,7 @@ class Call {
13916
13947
  // maintain the order of publishing tracks to restore them after a reconnection
13917
13948
  // it shouldn't contain duplicates
13918
13949
  this.trackPublishOrder = [];
13950
+ this.selfSubEnabled = false;
13919
13951
  this.hasJoinedOnce = false;
13920
13952
  this.deviceSettingsAppliedOnce = false;
13921
13953
  this.initialized = false;
@@ -14265,6 +14297,30 @@ class Call {
14265
14297
  await Promise.all(stopOnLeavePromises);
14266
14298
  });
14267
14299
  };
14300
+ /**
14301
+ * The largest video publish dimension across the current publish options.
14302
+ *
14303
+ * @internal
14304
+ */
14305
+ this.getMaxVideoPublishDimension = () => {
14306
+ if (!this.currentPublishOptions)
14307
+ return undefined;
14308
+ let maxDimension;
14309
+ let maxArea = 0;
14310
+ for (const opt of this.currentPublishOptions) {
14311
+ if (opt.trackType !== TrackType.VIDEO)
14312
+ continue;
14313
+ const dim = opt.videoDimension;
14314
+ if (!dim || !dim.width || !dim.height)
14315
+ continue;
14316
+ const area = dim.width * dim.height;
14317
+ if (area > maxArea) {
14318
+ maxDimension = dim;
14319
+ maxArea = area;
14320
+ }
14321
+ }
14322
+ return maxDimension;
14323
+ };
14268
14324
  /**
14269
14325
  * Update from the call response from the "call.ring" event
14270
14326
  * @internal
@@ -14411,7 +14467,7 @@ class Call {
14411
14467
  *
14412
14468
  * @returns a promise which resolves once the call join-flow has finished.
14413
14469
  */
14414
- this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, ...data } = {}) => {
14470
+ this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, selfSubEnabled = false, ...data } = {}) => {
14415
14471
  const callingState = this.state.callingState;
14416
14472
  if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
14417
14473
  throw new Error(`Illegal State: call.join() shall be called only once`);
@@ -14419,6 +14475,9 @@ class Call {
14419
14475
  if (data?.ring) {
14420
14476
  this.ringingSubject.next(true);
14421
14477
  }
14478
+ // we need this to be set before the callingx.joinCall() is
14479
+ // called to avoid registering the test call in the CallKit/Telecom
14480
+ this.selfSubEnabled = selfSubEnabled;
14422
14481
  const callingX = globalThis.streamRNVideoSDK?.callingX;
14423
14482
  if (callingX) {
14424
14483
  // for Android/iOS, we need to start the call in the callingx library as soon as possible
@@ -14808,7 +14867,9 @@ class Call {
14808
14867
  if (closePreviousInstances && this.publisher) {
14809
14868
  await this.publisher.dispose();
14810
14869
  }
14811
- this.publisher = new Publisher(basePeerConnectionOptions, publishOptions);
14870
+ this.publisher = new Publisher(basePeerConnectionOptions, publishOptions, {
14871
+ selfSubEnabled: this.selfSubEnabled,
14872
+ });
14812
14873
  }
14813
14874
  this.statsReporter?.stop();
14814
14875
  if (this.statsReportingIntervalInMs > 0) {
@@ -16304,6 +16365,12 @@ class Call {
16304
16365
  get currentUserId() {
16305
16366
  return this.clientStore.connectedUser?.id;
16306
16367
  }
16368
+ /**
16369
+ * A flag indicating whether self-subscription is enabled for the call.
16370
+ */
16371
+ get isSelfSubEnabled() {
16372
+ return this.selfSubEnabled;
16373
+ }
16307
16374
  /**
16308
16375
  * A flag indicating whether the call was created by the current user.
16309
16376
  */
@@ -17491,11 +17558,11 @@ class StreamClient {
17491
17558
  return await this.wsConnection.connect(this.defaultWSTimeout);
17492
17559
  };
17493
17560
  this.getSdkVersion = () => this.options.clientAppIdentifier?.sdkVersion ||
17494
- "1.54.0";
17561
+ "1.54.1-beta.0";
17495
17562
  this.getUserAgent = () => {
17496
17563
  if (!this.cachedUserAgent) {
17497
17564
  const { clientAppIdentifier = {} } = this.options;
17498
- const { sdkName = 'js', sdkVersion = "1.54.0", ...extras } = clientAppIdentifier;
17565
+ const { sdkName = 'js', sdkVersion = "1.54.1-beta.0", ...extras } = clientAppIdentifier;
17499
17566
  this.cachedUserAgent = [
17500
17567
  `stream-video-${sdkName}-v${sdkVersion}`,
17501
17568
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),