@stream-io/video-client 1.0.3 → 1.0.4

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.
@@ -6,14 +6,14 @@ export type SfuStatsReporterOptions = {
6
6
  options: StatsOptions;
7
7
  clientDetails: LocalClientDetailsType;
8
8
  subscriber: Subscriber;
9
- publisher: Publisher;
9
+ publisher?: Publisher;
10
10
  };
11
11
  export declare class SfuStatsReporter {
12
12
  private readonly logger;
13
13
  readonly options: StatsOptions;
14
14
  private readonly sfuClient;
15
15
  private readonly subscriber;
16
- private readonly publisher;
16
+ private readonly publisher?;
17
17
  private intervalId;
18
18
  private readonly sdkName;
19
19
  private readonly sdkVersion;
@@ -3,8 +3,9 @@ import { CallState } from '../store';
3
3
  import { Publisher, Subscriber } from '../rtc';
4
4
  export type StatsReporterOpts = {
5
5
  subscriber: Subscriber;
6
- publisher: Publisher;
6
+ publisher?: Publisher;
7
7
  state: CallState;
8
+ datacenter: string;
8
9
  pollingIntervalInMs?: number;
9
10
  };
10
11
  export type StatsReporter = {
@@ -43,7 +44,7 @@ export type StatsReporter = {
43
44
  /**
44
45
  * Creates a new StatsReporter instance that collects metrics about the ongoing call and reports them to the state store
45
46
  */
46
- export declare const createStatsReporter: ({ subscriber, publisher, state, pollingIntervalInMs, }: StatsReporterOpts) => StatsReporter;
47
+ export declare const createStatsReporter: ({ subscriber, publisher, state, datacenter, pollingIntervalInMs, }: StatsReporterOpts) => StatsReporter;
47
48
  export type StatsTransformOpts = {
48
49
  /**
49
50
  * The kind of track we are transforming stats for.
@@ -1,4 +1,5 @@
1
1
  import { LocalClientDetailsType } from '../client-details';
2
+ import { Sdk } from '../gen/video/sfu/models/models';
2
3
  /**
3
4
  * Flatten the stats report into an array of stats objects.
4
5
  *
@@ -15,3 +16,5 @@ export declare const getSdkSignature: (clientDetails: LocalClientDetailsType) =>
15
16
  sdkName: string;
16
17
  sdkVersion: string;
17
18
  };
19
+ export declare const getSdkName: (sdk: Sdk | undefined) => "stream-react" | "stream-react-native" | "stream-js";
20
+ export declare const getSdkVersion: (sdk: Sdk | undefined) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-client",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
package/src/Call.ts CHANGED
@@ -1001,7 +1001,10 @@ export class Call {
1001
1001
  });
1002
1002
  }
1003
1003
 
1004
- if (!this.publisher) {
1004
+ // anonymous users can't publish anything hence, there is no need
1005
+ // to create Publisher Peer Connection for them
1006
+ const isAnonymous = this.streamClient.user?.type === 'anonymous';
1007
+ if (!this.publisher && !isAnonymous) {
1005
1008
  const audioSettings = this.state.settings?.audio;
1006
1009
  const isDtxEnabled = !!audioSettings?.opus_dtx_enabled;
1007
1010
  const isRedEnabled = !!audioSettings?.redundant_coding_enabled;
@@ -1020,6 +1023,7 @@ export class Call {
1020
1023
  subscriber: this.subscriber,
1021
1024
  publisher: this.publisher,
1022
1025
  state: this.state,
1026
+ datacenter: this.sfuClient.edgeName,
1023
1027
  });
1024
1028
  }
1025
1029
 
@@ -1069,14 +1073,17 @@ export class Call {
1069
1073
  }
1070
1074
  if (isMigrating) {
1071
1075
  await this.subscriber.migrateTo(sfuClient, connectionConfig);
1072
- await this.publisher.migrateTo(sfuClient, connectionConfig);
1076
+ await this.publisher?.migrateTo(sfuClient, connectionConfig);
1073
1077
  } else if (isReconnecting) {
1074
1078
  if (reconnected) {
1075
1079
  // update the SFU client instance on the subscriber and publisher
1076
1080
  this.subscriber.setSfuClient(sfuClient);
1077
- this.publisher.setSfuClient(sfuClient);
1078
- // and perform a full ICE restart on the publisher
1079
- await this.publisher.restartIce();
1081
+ // publisher might not be there (anonymous users)
1082
+ if (this.publisher) {
1083
+ this.publisher.setSfuClient(sfuClient);
1084
+ // and perform a full ICE restart on the publisher
1085
+ await this.publisher.restartIce();
1086
+ }
1080
1087
  } else if (previousSfuClient?.isFastReconnecting) {
1081
1088
  // reconnection wasn't possible, so we need to do a full rejoin
1082
1089
  return await reconnect('full', 're-attempting').catch((err) => {
@@ -2,15 +2,14 @@ import { StreamSfuClient } from '../StreamSfuClient';
2
2
  import { StatsOptions } from '../gen/coordinator';
3
3
  import { getLogger } from '../logger';
4
4
  import { Publisher, Subscriber } from '../rtc';
5
- import { SdkType } from '../gen/video/sfu/models/models';
6
- import { flatten } from './utils';
5
+ import { flatten, getSdkName, getSdkVersion } from './utils';
7
6
  import { getWebRTCInfo, LocalClientDetailsType } from '../client-details';
8
7
 
9
8
  export type SfuStatsReporterOptions = {
10
9
  options: StatsOptions;
11
10
  clientDetails: LocalClientDetailsType;
12
11
  subscriber: Subscriber;
13
- publisher: Publisher;
12
+ publisher?: Publisher;
14
13
  };
15
14
 
16
15
  export class SfuStatsReporter {
@@ -20,7 +19,7 @@ export class SfuStatsReporter {
20
19
 
21
20
  private readonly sfuClient: StreamSfuClient;
22
21
  private readonly subscriber: Subscriber;
23
- private readonly publisher: Publisher;
22
+ private readonly publisher?: Publisher;
24
23
 
25
24
  private intervalId: NodeJS.Timeout | undefined;
26
25
  private readonly sdkName: string;
@@ -39,16 +38,8 @@ export class SfuStatsReporter {
39
38
 
40
39
  const { sdk, browser } = clientDetails;
41
40
 
42
- this.sdkName =
43
- sdk && sdk.type === SdkType.REACT
44
- ? 'stream-react'
45
- : sdk && sdk.type === SdkType.REACT_NATIVE
46
- ? 'stream-react-native'
47
- : 'stream-js';
48
-
49
- this.sdkVersion = sdk
50
- ? `${sdk.major}.${sdk.minor}.${sdk.patch}`
51
- : '0.0.0-development';
41
+ this.sdkName = getSdkName(sdk);
42
+ this.sdkVersion = getSdkVersion(sdk);
52
43
 
53
44
  // The WebRTC version if passed from the SDK, it is taken else the browser info is sent.
54
45
  this.webRTCVersion =
@@ -60,7 +51,7 @@ export class SfuStatsReporter {
60
51
  private run = async () => {
61
52
  const [subscriberStats, publisherStats] = await Promise.all([
62
53
  this.subscriber.getStats().then(flatten).then(JSON.stringify),
63
- this.publisher.getStats().then(flatten).then(JSON.stringify),
54
+ this.publisher?.getStats().then(flatten).then(JSON.stringify) ?? '[]',
64
55
  ]);
65
56
 
66
57
  await this.sfuClient.sendStats({
@@ -11,8 +11,9 @@ import { flatten } from './utils';
11
11
 
12
12
  export type StatsReporterOpts = {
13
13
  subscriber: Subscriber;
14
- publisher: Publisher;
14
+ publisher?: Publisher;
15
15
  state: CallState;
16
+ datacenter: string;
16
17
  pollingIntervalInMs?: number;
17
18
  };
18
19
 
@@ -67,6 +68,7 @@ export const createStatsReporter = ({
67
68
  subscriber,
68
69
  publisher,
69
70
  state,
71
+ datacenter,
70
72
  pollingIntervalInMs = 2000,
71
73
  }: StatsReporterOpts): StatsReporter => {
72
74
  const logger = getLogger(['stats']);
@@ -79,7 +81,6 @@ export const createStatsReporter = ({
79
81
  } else if (kind === 'publisher' && publisher) {
80
82
  return publisher.getStats(selector);
81
83
  } else {
82
- logger('warn', `Can't retrieve RTC stats for ${kind}`);
83
84
  return undefined;
84
85
  }
85
86
  };
@@ -89,6 +90,7 @@ export const createStatsReporter = ({
89
90
  mediaStream: MediaStream,
90
91
  ) => {
91
92
  const pc = kind === 'subscriber' ? subscriber : publisher;
93
+ if (!pc) return [];
92
94
  const statsForStream: StatsReport[] = [];
93
95
  for (let track of mediaStream.getTracks()) {
94
96
  const report = await pc.getStats(track);
@@ -141,7 +143,7 @@ export const createStatsReporter = ({
141
143
  } catch (e) {
142
144
  logger(
143
145
  'error',
144
- `Failed to collect stats for ${kind} if ${participant.userId}`,
146
+ `Failed to collect stats for ${kind} of ${participant.userId}`,
145
147
  e,
146
148
  );
147
149
  }
@@ -159,23 +161,25 @@ export const createStatsReporter = ({
159
161
  )
160
162
  .then(aggregate),
161
163
  publisher
162
- .getStats()
163
- .then((report) =>
164
- transform(report, {
165
- kind: 'publisher',
166
- trackKind: 'video',
167
- }),
168
- )
169
- .then(aggregate),
164
+ ? publisher
165
+ .getStats()
166
+ .then((report) =>
167
+ transform(report, {
168
+ kind: 'publisher',
169
+ trackKind: 'video',
170
+ }),
171
+ )
172
+ .then(aggregate)
173
+ : getEmptyStats(),
170
174
  ]);
171
175
 
172
176
  const [subscriberRawStats, publisherRawStats] = await Promise.all([
173
177
  getRawStatsForTrack('subscriber'),
174
- getRawStatsForTrack('publisher'),
178
+ publisher ? getRawStatsForTrack('publisher') : undefined,
175
179
  ]);
176
180
 
177
181
  state.setCallStatsReport({
178
- datacenter: publisher.sfuClient.edgeName,
182
+ datacenter,
179
183
  publisherStats,
180
184
  subscriberStats,
181
185
  subscriberRawStats,
@@ -288,14 +292,9 @@ const transform = (
288
292
  };
289
293
  };
290
294
 
291
- /**
292
- * Aggregates generic stats.
293
- *
294
- * @param stats the stats to aggregate.
295
- */
296
- const aggregate = (stats: StatsReport): AggregatedStatsReport => {
297
- const aggregatedStats: AggregatedStatsReport = {
298
- rawReport: stats,
295
+ const getEmptyStats = (stats?: StatsReport): AggregatedStatsReport => {
296
+ return {
297
+ rawReport: stats ?? { streams: [], timestamp: Date.now() },
299
298
  totalBytesSent: 0,
300
299
  totalBytesReceived: 0,
301
300
  averageJitterInMs: 0,
@@ -306,6 +305,15 @@ const aggregate = (stats: StatsReport): AggregatedStatsReport => {
306
305
  highestFramesPerSecond: 0,
307
306
  timestamp: Date.now(),
308
307
  };
308
+ };
309
+
310
+ /**
311
+ * Aggregates generic stats.
312
+ *
313
+ * @param stats the stats to aggregate.
314
+ */
315
+ const aggregate = (stats: StatsReport): AggregatedStatsReport => {
316
+ const aggregatedStats = getEmptyStats(stats);
309
317
 
310
318
  let maxArea = -1;
311
319
  const area = (w: number, h: number) => w * h;
@@ -1,5 +1,5 @@
1
1
  import { LocalClientDetailsType } from '../client-details';
2
- import { SdkType } from '../gen/video/sfu/models/models';
2
+ import { Sdk, SdkType } from '../gen/video/sfu/models/models';
3
3
 
4
4
  /**
5
5
  * Flatten the stats report into an array of stats objects.
@@ -16,16 +16,8 @@ export const flatten = (report: RTCStatsReport) => {
16
16
 
17
17
  export const getSdkSignature = (clientDetails: LocalClientDetailsType) => {
18
18
  const { sdk, ...platform } = clientDetails;
19
- const sdkName =
20
- sdk && sdk.type === SdkType.REACT
21
- ? 'stream-react'
22
- : sdk && sdk.type === SdkType.REACT_NATIVE
23
- ? 'stream-react-native'
24
- : 'stream-js';
25
-
26
- const sdkVersion = sdk
27
- ? `${sdk.major}.${sdk.minor}.${sdk.patch}`
28
- : '0.0.0-development';
19
+ const sdkName = getSdkName(sdk);
20
+ const sdkVersion = getSdkVersion(sdk);
29
21
 
30
22
  return {
31
23
  sdkName,
@@ -33,3 +25,15 @@ export const getSdkSignature = (clientDetails: LocalClientDetailsType) => {
33
25
  ...platform,
34
26
  };
35
27
  };
28
+
29
+ export const getSdkName = (sdk: Sdk | undefined) => {
30
+ return sdk && sdk.type === SdkType.REACT
31
+ ? 'stream-react'
32
+ : sdk && sdk.type === SdkType.REACT_NATIVE
33
+ ? 'stream-react-native'
34
+ : 'stream-js';
35
+ };
36
+
37
+ export const getSdkVersion = (sdk: Sdk | undefined) => {
38
+ return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
39
+ };