@stream-io/video-client 1.52.0 → 1.52.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.52.0";
6652
+ const version = "1.52.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
 
@@ -8409,7 +8417,7 @@ class Publisher extends BasePeerConnection {
8409
8417
  /**
8410
8418
  * Constructs a new `Publisher` instance.
8411
8419
  */
8412
- constructor(baseOptions, publishOptions) {
8420
+ constructor(baseOptions, publishOptions, opts = {}) {
8413
8421
  super(PeerType.PUBLISHER_UNSPECIFIED, baseOptions);
8414
8422
  this.transceiverCache = new TransceiverCache();
8415
8423
  this.clonedTracks = new Set();
@@ -8830,6 +8838,7 @@ class Publisher extends BasePeerConnection {
8830
8838
  muted: !isTrackLive,
8831
8839
  codec: publishOption.codec,
8832
8840
  publishOptionId: publishOption.id,
8841
+ selfSubAudioVideo: this.selfSubEnabled,
8833
8842
  };
8834
8843
  };
8835
8844
  this.cloneTrack = (track) => {
@@ -8910,6 +8919,7 @@ class Publisher extends BasePeerConnection {
8910
8919
  });
8911
8920
  };
8912
8921
  this.publishOptions = publishOptions;
8922
+ this.selfSubEnabled = opts.selfSubEnabled ?? false;
8913
8923
  this.on('iceRestart', (iceRestart) => {
8914
8924
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
8915
8925
  return;
@@ -8991,6 +9001,13 @@ class Subscriber extends BasePeerConnection {
8991
9001
  */
8992
9002
  constructor(opts) {
8993
9003
  super(PeerType.SUBSCRIBER, opts);
9004
+ /**
9005
+ * Remote streams received from the SFU. For a self-sub case
9006
+ * we need to be able to distinguish between the local capture stream.
9007
+ * The map will never contain local streams so we can safely use it to
9008
+ * check if the stream is remote and dispose it when needed.
9009
+ */
9010
+ this.trackedStreams = new WeakSet();
8994
9011
  /**
8995
9012
  * Restarts the ICE connection and renegotiates with the SFU.
8996
9013
  */
@@ -9025,6 +9042,7 @@ class Subscriber extends BasePeerConnection {
9025
9042
  // example: `e3f6aaf8-b03d-4911-be36-83f47d37a76a:TRACK_TYPE_VIDEO`
9026
9043
  const [trackId, rawTrackType] = primaryStream.id.split(':');
9027
9044
  const participantToUpdate = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
9045
+ const isSelfSub = !!participantToUpdate?.isLocalParticipant;
9028
9046
  this.logger.debug(`[onTrack]: Got remote ${rawTrackType} track for userId: ${participantToUpdate?.userId}`, track.id, track);
9029
9047
  const trackType = toTrackType(rawTrackType);
9030
9048
  if (!trackType) {
@@ -9048,6 +9066,9 @@ class Subscriber extends BasePeerConnection {
9048
9066
  this.setRemoteTrackInterrupted(trackId, trackType, true);
9049
9067
  }
9050
9068
  this.trackIdToTrackType.set(track.id, trackType);
9069
+ if (isSelfSub) {
9070
+ this.trackedStreams.add(primaryStream);
9071
+ }
9051
9072
  if (!participantToUpdate) {
9052
9073
  this.logger.warn(`[onTrack]: Received track for unknown participant: ${trackId}`, e);
9053
9074
  this.state.registerOrphanedTrack({
@@ -9063,6 +9084,12 @@ class Subscriber extends BasePeerConnection {
9063
9084
  this.logger.error(`Unknown track type: ${rawTrackType}`);
9064
9085
  return;
9065
9086
  }
9087
+ // Self-sub loopback audio routes to the speaker by default, which
9088
+ // would echo the local user's voice. Default-mute here; consumers
9089
+ // (the loopback recording hook) re-enable explicitly when needed.
9090
+ if (isSelfSub && e.track.kind === 'audio') {
9091
+ e.track.enabled = false;
9092
+ }
9066
9093
  // get the previous stream to dispose it later
9067
9094
  // usually this happens during migration, when the stream is replaced
9068
9095
  // with a new one but the old one is still in the state
@@ -9071,8 +9098,12 @@ class Subscriber extends BasePeerConnection {
9071
9098
  this.state.updateParticipant(participantToUpdate.sessionId, {
9072
9099
  [streamKindProp]: primaryStream,
9073
9100
  });
9074
- // now, dispose the previous stream if it exists
9075
9101
  if (previousStream) {
9102
+ if (isSelfSub && !this.trackedStreams.has(previousStream)) {
9103
+ // this is the local capture stream, we don't want to dispose it
9104
+ this.logger.debug(`[onTrack]: Skipping cleanup of previous ${e.track.kind} stream for userId: ${participantToUpdate.userId} because it is not tracked`);
9105
+ return;
9106
+ }
9076
9107
  this.logger.info(`[onTrack]: Cleaning up previous remote ${track.kind} tracks for userId: ${participantToUpdate.userId}`);
9077
9108
  previousStream.getTracks().forEach((t) => {
9078
9109
  t.stop();
@@ -13830,6 +13861,7 @@ class Call {
13830
13861
  // maintain the order of publishing tracks to restore them after a reconnection
13831
13862
  // it shouldn't contain duplicates
13832
13863
  this.trackPublishOrder = [];
13864
+ this.selfSubEnabled = false;
13833
13865
  this.hasJoinedOnce = false;
13834
13866
  this.deviceSettingsAppliedOnce = false;
13835
13867
  this.initialized = false;
@@ -14174,6 +14206,30 @@ class Call {
14174
14206
  await Promise.all(stopOnLeavePromises);
14175
14207
  });
14176
14208
  };
14209
+ /**
14210
+ * The largest video publish dimension across the current publish options.
14211
+ *
14212
+ * @internal
14213
+ */
14214
+ this.getMaxVideoPublishDimension = () => {
14215
+ if (!this.currentPublishOptions)
14216
+ return undefined;
14217
+ let maxDimension;
14218
+ let maxArea = 0;
14219
+ for (const opt of this.currentPublishOptions) {
14220
+ if (opt.trackType !== TrackType.VIDEO)
14221
+ continue;
14222
+ const dim = opt.videoDimension;
14223
+ if (!dim || !dim.width || !dim.height)
14224
+ continue;
14225
+ const area = dim.width * dim.height;
14226
+ if (area > maxArea) {
14227
+ maxDimension = dim;
14228
+ maxArea = area;
14229
+ }
14230
+ }
14231
+ return maxDimension;
14232
+ };
14177
14233
  /**
14178
14234
  * Update from the call response from the "call.ring" event
14179
14235
  * @internal
@@ -14320,7 +14376,7 @@ class Call {
14320
14376
  *
14321
14377
  * @returns a promise which resolves once the call join-flow has finished.
14322
14378
  */
14323
- this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, ...data } = {}) => {
14379
+ this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, selfSubEnabled = false, ...data } = {}) => {
14324
14380
  const callingState = this.state.callingState;
14325
14381
  if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
14326
14382
  throw new Error(`Illegal State: call.join() shall be called only once`);
@@ -14328,6 +14384,9 @@ class Call {
14328
14384
  if (data?.ring) {
14329
14385
  this.ringingSubject.next(true);
14330
14386
  }
14387
+ // we need this to be set before the callingx.joinCall() is
14388
+ // called to avoid registering the test call in the CallKit/Telecom
14389
+ this.selfSubEnabled = selfSubEnabled;
14331
14390
  const callingX = globalThis.streamRNVideoSDK?.callingX;
14332
14391
  if (callingX) {
14333
14392
  // for Android/iOS, we need to start the call in the callingx library as soon as possible
@@ -14695,7 +14754,9 @@ class Call {
14695
14754
  if (closePreviousInstances && this.publisher) {
14696
14755
  await this.publisher.dispose();
14697
14756
  }
14698
- this.publisher = new Publisher(basePeerConnectionOptions, publishOptions);
14757
+ this.publisher = new Publisher(basePeerConnectionOptions, publishOptions, {
14758
+ selfSubEnabled: this.selfSubEnabled,
14759
+ });
14699
14760
  }
14700
14761
  this.statsReporter?.stop();
14701
14762
  if (this.statsReportingIntervalInMs > 0) {
@@ -16125,6 +16186,12 @@ class Call {
16125
16186
  get currentUserId() {
16126
16187
  return this.clientStore.connectedUser?.id;
16127
16188
  }
16189
+ /**
16190
+ * A flag indicating whether self-subscription is enabled for the call.
16191
+ */
16192
+ get isSelfSubEnabled() {
16193
+ return this.selfSubEnabled;
16194
+ }
16128
16195
  /**
16129
16196
  * A flag indicating whether the call was created by the current user.
16130
16197
  */
@@ -17314,7 +17381,7 @@ class StreamClient {
17314
17381
  this.getUserAgent = () => {
17315
17382
  if (!this.cachedUserAgent) {
17316
17383
  const { clientAppIdentifier = {} } = this.options;
17317
- const { sdkName = 'js', sdkVersion = "1.52.0", ...extras } = clientAppIdentifier;
17384
+ const { sdkName = 'js', sdkVersion = "1.52.1-beta.0", ...extras } = clientAppIdentifier;
17318
17385
  this.cachedUserAgent = [
17319
17386
  `stream-video-${sdkName}-v${sdkVersion}`,
17320
17387
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),