@stream-io/video-client 1.41.1 → 1.41.3

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
@@ -6208,7 +6208,7 @@ const getSdkVersion = (sdk) => {
6208
6208
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6209
6209
  };
6210
6210
 
6211
- const version = "1.41.1";
6211
+ const version = "1.41.3";
6212
6212
  const [major, minor, patch] = version.split('.');
6213
6213
  let sdkInfo = {
6214
6214
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -6531,6 +6531,16 @@ const createStatsReporter = ({ subscriber, publisher, state, datacenter, polling
6531
6531
  stop,
6532
6532
  };
6533
6533
  };
6534
+ /**
6535
+ * Extracts camera statistics from a media source.
6536
+ *
6537
+ * @param mediaSource the media source stats to extract camera info from.
6538
+ */
6539
+ const getCameraStats = (mediaSource) => ({
6540
+ frameRate: mediaSource.framesPerSecond,
6541
+ frameWidth: mediaSource.width,
6542
+ frameHeight: mediaSource.height,
6543
+ });
6534
6544
  /**
6535
6545
  * Transforms raw RTC stats into a slimmer and uniform across browsers format.
6536
6546
  *
@@ -6556,6 +6566,7 @@ const transform = (report, opts) => {
6556
6566
  }
6557
6567
  let trackType;
6558
6568
  let audioLevel;
6569
+ let camera;
6559
6570
  let concealedSamples;
6560
6571
  let concealmentEvents;
6561
6572
  let packetsReceived;
@@ -6571,6 +6582,9 @@ const transform = (report, opts) => {
6571
6582
  typeof mediaSource.audioLevel === 'number') {
6572
6583
  audioLevel = mediaSource.audioLevel;
6573
6584
  }
6585
+ if (trackKind === 'video') {
6586
+ camera = getCameraStats(mediaSource);
6587
+ }
6574
6588
  }
6575
6589
  }
6576
6590
  else if (kind === 'subscriber' && trackKind === 'audio') {
@@ -6604,6 +6618,7 @@ const transform = (report, opts) => {
6604
6618
  concealmentEvents,
6605
6619
  packetsReceived,
6606
6620
  packetsLost,
6621
+ camera,
6607
6622
  };
6608
6623
  });
6609
6624
  return {
@@ -6623,6 +6638,7 @@ const getEmptyVideoStats = (stats) => {
6623
6638
  highestFrameWidth: 0,
6624
6639
  highestFrameHeight: 0,
6625
6640
  highestFramesPerSecond: 0,
6641
+ camera: {},
6626
6642
  codec: '',
6627
6643
  codecPerTrackType: {},
6628
6644
  timestamp: Date.now(),
@@ -6667,6 +6683,9 @@ const aggregate = (stats) => {
6667
6683
  acc.highestFramesPerSecond = stream.framesPerSecond || 0;
6668
6684
  maxArea = streamArea;
6669
6685
  }
6686
+ if (stream.trackType === TrackType.VIDEO) {
6687
+ acc.camera = stream.camera;
6688
+ }
6670
6689
  qualityLimitationReasons.add(stream.qualityLimitationReason || '');
6671
6690
  return acc;
6672
6691
  }, aggregatedStats);
@@ -6731,6 +6750,7 @@ const aggregateAudio = (stats) => {
6731
6750
  class SfuStatsReporter {
6732
6751
  constructor(sfuClient, { options, clientDetails, subscriber, publisher, microphone, camera, state, tracer, unifiedSessionId, }) {
6733
6752
  this.logger = videoLoggerSystem.getLogger('SfuStatsReporter');
6753
+ this.reportCount = 0;
6734
6754
  this.inputDevices = new Map();
6735
6755
  this.observeDevice = (device, kind) => {
6736
6756
  const { browserPermissionState$ } = device.state;
@@ -6832,17 +6852,31 @@ class SfuStatsReporter {
6832
6852
  throw err;
6833
6853
  }
6834
6854
  };
6855
+ this.scheduleNextReport = () => {
6856
+ const intervals = [1500, 3000, 3000, 5000];
6857
+ if (this.reportCount < intervals.length) {
6858
+ this.timeoutId = setTimeout(() => {
6859
+ this.flush();
6860
+ this.reportCount++;
6861
+ this.scheduleNextReport();
6862
+ }, intervals[this.reportCount]);
6863
+ }
6864
+ else {
6865
+ clearInterval(this.intervalId);
6866
+ this.intervalId = setInterval(() => {
6867
+ this.flush();
6868
+ }, this.options.reporting_interval_ms);
6869
+ }
6870
+ };
6835
6871
  this.start = () => {
6836
6872
  if (this.options.reporting_interval_ms <= 0)
6837
6873
  return;
6838
6874
  this.observeDevice(this.microphone, 'mic');
6839
6875
  this.observeDevice(this.camera, 'camera');
6876
+ this.reportCount = 0;
6840
6877
  clearInterval(this.intervalId);
6841
- this.intervalId = setInterval(() => {
6842
- this.run().catch((err) => {
6843
- this.logger.warn('Failed to report stats', err);
6844
- });
6845
- }, this.options.reporting_interval_ms);
6878
+ clearTimeout(this.timeoutId);
6879
+ this.scheduleNextReport();
6846
6880
  };
6847
6881
  this.stop = () => {
6848
6882
  this.unsubscribeDevicePermissionsSubscription?.();
@@ -6854,20 +6888,13 @@ class SfuStatsReporter {
6854
6888
  this.intervalId = undefined;
6855
6889
  clearTimeout(this.timeoutId);
6856
6890
  this.timeoutId = undefined;
6891
+ this.reportCount = 0;
6857
6892
  };
6858
6893
  this.flush = () => {
6859
6894
  this.run().catch((err) => {
6860
6895
  this.logger.warn('Failed to flush report stats', err);
6861
6896
  });
6862
6897
  };
6863
- this.scheduleOne = (timeout) => {
6864
- clearTimeout(this.timeoutId);
6865
- this.timeoutId = setTimeout(() => {
6866
- this.run().catch((err) => {
6867
- this.logger.warn('Failed to report stats', err);
6868
- });
6869
- }, timeout);
6870
- };
6871
6898
  this.sfuClient = sfuClient;
6872
6899
  this.options = options;
6873
6900
  this.subscriber = subscriber;
@@ -12112,9 +12139,7 @@ class SpeakerManager {
12112
12139
  * @returns an Observable that will be updated if a device is connected or disconnected
12113
12140
  */
12114
12141
  listDevices() {
12115
- if (isReactNative()) {
12116
- throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
12117
- }
12142
+ assertUnsupportedInReactNative();
12118
12143
  return getAudioOutputDevices(this.call.tracer);
12119
12144
  }
12120
12145
  /**
@@ -12125,9 +12150,7 @@ class SpeakerManager {
12125
12150
  * @param deviceId empty string means the system default
12126
12151
  */
12127
12152
  select(deviceId) {
12128
- if (isReactNative()) {
12129
- throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
12130
- }
12153
+ assertUnsupportedInReactNative();
12131
12154
  this.state.setDevice(deviceId);
12132
12155
  }
12133
12156
  /**
@@ -12137,9 +12160,7 @@ class SpeakerManager {
12137
12160
  * Note: This method is not supported in React Native
12138
12161
  */
12139
12162
  setVolume(volume) {
12140
- if (isReactNative()) {
12141
- throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
12142
- }
12163
+ assertUnsupportedInReactNative();
12143
12164
  if (volume && (volume < 0 || volume > 1)) {
12144
12165
  throw new Error('Volume must be between 0 and 1');
12145
12166
  }
@@ -12166,6 +12187,11 @@ class SpeakerManager {
12166
12187
  });
12167
12188
  }
12168
12189
  }
12190
+ const assertUnsupportedInReactNative = () => {
12191
+ if (isReactNative()) {
12192
+ throw new Error('Unsupported in React Native. See: https://getstream.io/video/docs/react-native/guides/camera-and-microphone/#speaker-management');
12193
+ }
12194
+ };
12169
12195
 
12170
12196
  /**
12171
12197
  * An object representation of a `Call`.
@@ -12468,6 +12494,8 @@ class Call {
12468
12494
  }
12469
12495
  this.statsReporter?.stop();
12470
12496
  this.statsReporter = undefined;
12497
+ const leaveReason = message ?? reason ?? 'user is leaving the call';
12498
+ this.tracer.trace('call.leaveReason', leaveReason);
12471
12499
  this.sfuStatsReporter?.flush();
12472
12500
  this.sfuStatsReporter?.stop();
12473
12501
  this.sfuStatsReporter = undefined;
@@ -12475,7 +12503,7 @@ class Call {
12475
12503
  this.subscriber = undefined;
12476
12504
  this.publisher?.dispose();
12477
12505
  this.publisher = undefined;
12478
- await this.sfuClient?.leaveAndClose(message ?? reason ?? 'user is leaving the call');
12506
+ await this.sfuClient?.leaveAndClose(leaveReason);
12479
12507
  this.sfuClient = undefined;
12480
12508
  this.dynascaleManager.setSfuClient(undefined);
12481
12509
  await this.dynascaleManager.dispose();
@@ -12617,6 +12645,7 @@ class Call {
12617
12645
  * Unless you are implementing a custom "ringing" flow, you should not use this method.
12618
12646
  */
12619
12647
  this.accept = async () => {
12648
+ this.tracer.trace('call.accept', '');
12620
12649
  return this.streamClient.post(`${this.streamClientBasePath}/accept`);
12621
12650
  };
12622
12651
  /**
@@ -12629,6 +12658,7 @@ class Call {
12629
12658
  * @param reason the reason for rejecting the call.
12630
12659
  */
12631
12660
  this.reject = async (reason = 'decline') => {
12661
+ this.tracer.trace('call.reject', reason);
12632
12662
  return this.streamClient.post(`${this.streamClientBasePath}/reject`, { reason: reason });
12633
12663
  };
12634
12664
  /**
@@ -13392,11 +13422,6 @@ class Call {
13392
13422
  trackTypes.push(screenShareAudio);
13393
13423
  }
13394
13424
  }
13395
- if (track.kind === 'video') {
13396
- // schedules calibration report - the SFU will use the performance stats
13397
- // to adjust the quality thresholds as early as possible
13398
- this.sfuStatsReporter?.scheduleOne(3000);
13399
- }
13400
13425
  await this.updateLocalStreamState(mediaStream, ...trackTypes);
13401
13426
  };
13402
13427
  /**
@@ -15339,7 +15364,7 @@ class StreamClient {
15339
15364
  this.getUserAgent = () => {
15340
15365
  if (!this.cachedUserAgent) {
15341
15366
  const { clientAppIdentifier = {} } = this.options;
15342
- const { sdkName = 'js', sdkVersion = "1.41.1", ...extras } = clientAppIdentifier;
15367
+ const { sdkName = 'js', sdkVersion = "1.41.3", ...extras } = clientAppIdentifier;
15343
15368
  this.cachedUserAgent = [
15344
15369
  `stream-video-${sdkName}-v${sdkVersion}`,
15345
15370
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),