@stream-io/video-client 1.51.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.browser.es.js +382 -18
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +382 -17
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +382 -18
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/Call.d.ts +13 -1
  9. package/dist/src/gen/google/protobuf/struct.d.ts +3 -1
  10. package/dist/src/gen/google/protobuf/timestamp.d.ts +3 -1
  11. package/dist/src/gen/video/sfu/event/events.d.ts +26 -1
  12. package/dist/src/gen/video/sfu/models/models.d.ts +208 -2
  13. package/dist/src/gen/video/sfu/signal_rpc/signal.client.d.ts +31 -2
  14. package/dist/src/gen/video/sfu/signal_rpc/signal.d.ts +67 -1
  15. package/dist/src/helpers/participantUtils.d.ts +10 -0
  16. package/dist/src/rtc/BasePeerConnection.d.ts +1 -1
  17. package/dist/src/rtc/Publisher.d.ts +4 -1
  18. package/dist/src/rtc/Subscriber.d.ts +7 -0
  19. package/dist/src/rtc/types.d.ts +1 -0
  20. package/dist/src/stats/rtc/StatsTracer.d.ts +2 -1
  21. package/dist/src/stats/utils.d.ts +1 -0
  22. package/package.json +14 -14
  23. package/src/Call.ts +51 -2
  24. package/src/devices/__tests__/CameraManager.test.ts +3 -1
  25. package/src/devices/__tests__/DeviceManager.test.ts +3 -1
  26. package/src/devices/__tests__/MicrophoneManager.test.ts +3 -1
  27. package/src/devices/__tests__/MicrophoneManagerRN.test.ts +3 -1
  28. package/src/devices/__tests__/ScreenShareManager.test.ts +3 -1
  29. package/src/devices/__tests__/web-audio.mocks.ts +3 -1
  30. package/src/gen/google/protobuf/struct.ts +7 -12
  31. package/src/gen/google/protobuf/timestamp.ts +6 -7
  32. package/src/gen/video/sfu/event/events.ts +33 -25
  33. package/src/gen/video/sfu/models/models.ts +349 -1
  34. package/src/gen/video/sfu/signal_rpc/signal.client.ts +51 -29
  35. package/src/gen/video/sfu/signal_rpc/signal.ts +122 -15
  36. package/src/helpers/__tests__/DynascaleManager.test.ts +8 -7
  37. package/src/helpers/__tests__/browsers.test.ts +4 -4
  38. package/src/helpers/__tests__/participantUtils.test.ts +47 -0
  39. package/src/helpers/client-details.ts +4 -1
  40. package/src/helpers/participantUtils.ts +15 -0
  41. package/src/rtc/BasePeerConnection.ts +7 -1
  42. package/src/rtc/Publisher.ts +4 -0
  43. package/src/rtc/Subscriber.ts +29 -1
  44. package/src/rtc/__tests__/Subscriber.test.ts +5 -1
  45. package/src/rtc/__tests__/mocks/webrtc.mocks.ts +16 -15
  46. package/src/rtc/types.ts +1 -0
  47. package/src/stats/rtc/StatsTracer.ts +25 -4
  48. package/src/stats/rtc/__tests__/StatsTracer.test.ts +155 -0
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
@@ -1187,6 +1189,14 @@ var SdkType;
1187
1189
  * @generated from protobuf enum value: SDK_TYPE_PLAIN_JAVASCRIPT = 9;
1188
1190
  */
1189
1191
  SdkType[SdkType["PLAIN_JAVASCRIPT"] = 9] = "PLAIN_JAVASCRIPT";
1192
+ /**
1193
+ * @generated from protobuf enum value: SDK_TYPE_PYTHON = 10;
1194
+ */
1195
+ SdkType[SdkType["PYTHON"] = 10] = "PYTHON";
1196
+ /**
1197
+ * @generated from protobuf enum value: SDK_TYPE_VISION_AGENTS = 11;
1198
+ */
1199
+ SdkType[SdkType["VISION_AGENTS"] = 11] = "VISION_AGENTS";
1190
1200
  })(SdkType || (SdkType = {}));
1191
1201
  /**
1192
1202
  * @generated from protobuf enum stream.video.sfu.models.TrackUnpublishReason
@@ -1398,6 +1408,12 @@ var ClientCapability;
1398
1408
  * @generated from protobuf enum value: CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE = 1;
1399
1409
  */
1400
1410
  ClientCapability[ClientCapability["SUBSCRIBER_VIDEO_PAUSE"] = 1] = "SUBSCRIBER_VIDEO_PAUSE";
1411
+ /**
1412
+ * Instructs SFU that stats will be sent to the coordinator
1413
+ *
1414
+ * @generated from protobuf enum value: CLIENT_CAPABILITY_COORDINATOR_STATS = 2;
1415
+ */
1416
+ ClientCapability[ClientCapability["COORDINATOR_STATS"] = 2] = "COORDINATOR_STATS";
1401
1417
  })(ClientCapability || (ClientCapability = {}));
1402
1418
  /**
1403
1419
  * DegradationPreference represents the RTCDegradationPreference from WebRTC.
@@ -1825,6 +1841,12 @@ class TrackInfo$Type extends MessageType {
1825
1841
  kind: 'scalar',
1826
1842
  T: 5 /*ScalarType.INT32*/,
1827
1843
  },
1844
+ {
1845
+ no: 13,
1846
+ name: 'self_sub_audio_video',
1847
+ kind: 'scalar',
1848
+ T: 8 /*ScalarType.BOOL*/,
1849
+ },
1828
1850
  ]);
1829
1851
  }
1830
1852
  }
@@ -1863,6 +1885,12 @@ class ClientDetails$Type extends MessageType {
1863
1885
  { no: 2, name: 'os', kind: 'message', T: () => OS },
1864
1886
  { no: 3, name: 'browser', kind: 'message', T: () => Browser },
1865
1887
  { no: 4, name: 'device', kind: 'message', T: () => Device },
1888
+ {
1889
+ no: 5,
1890
+ name: 'webrtc_version',
1891
+ kind: 'scalar',
1892
+ T: 9 /*ScalarType.STRING*/,
1893
+ },
1866
1894
  ]);
1867
1895
  }
1868
1896
  }
@@ -2135,6 +2163,171 @@ class PerformanceStats$Type extends MessageType {
2135
2163
  * @generated MessageType for protobuf message stream.video.sfu.models.PerformanceStats
2136
2164
  */
2137
2165
  const PerformanceStats = new PerformanceStats$Type();
2166
+ // @generated message type with reflection information, may provide speed optimized methods
2167
+ class RtpBase$Type extends MessageType {
2168
+ constructor() {
2169
+ super('stream.video.sfu.models.RtpBase', [
2170
+ { no: 1, name: 'ssrc', kind: 'scalar', T: 13 /*ScalarType.UINT32*/ },
2171
+ { no: 2, name: 'kind', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2172
+ {
2173
+ no: 3,
2174
+ name: 'timestamp_ms',
2175
+ kind: 'scalar',
2176
+ T: 1 /*ScalarType.DOUBLE*/,
2177
+ },
2178
+ ]);
2179
+ }
2180
+ }
2181
+ /**
2182
+ * @generated MessageType for protobuf message stream.video.sfu.models.RtpBase
2183
+ */
2184
+ const RtpBase = new RtpBase$Type();
2185
+ // @generated message type with reflection information, may provide speed optimized methods
2186
+ class InboundRtp$Type extends MessageType {
2187
+ constructor() {
2188
+ super('stream.video.sfu.models.InboundRtp', [
2189
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2190
+ {
2191
+ no: 2,
2192
+ name: 'jitter_seconds',
2193
+ kind: 'scalar',
2194
+ T: 1 /*ScalarType.DOUBLE*/,
2195
+ },
2196
+ {
2197
+ no: 3,
2198
+ name: 'packets_received',
2199
+ kind: 'scalar',
2200
+ T: 4 /*ScalarType.UINT64*/,
2201
+ },
2202
+ {
2203
+ no: 4,
2204
+ name: 'packets_lost',
2205
+ kind: 'scalar',
2206
+ T: 4 /*ScalarType.UINT64*/,
2207
+ },
2208
+ {
2209
+ no: 5,
2210
+ name: 'packet_loss_percent',
2211
+ kind: 'scalar',
2212
+ T: 1 /*ScalarType.DOUBLE*/,
2213
+ },
2214
+ {
2215
+ no: 10,
2216
+ name: 'concealment_events',
2217
+ kind: 'scalar',
2218
+ T: 13 /*ScalarType.UINT32*/,
2219
+ },
2220
+ {
2221
+ no: 11,
2222
+ name: 'concealment_percent',
2223
+ kind: 'scalar',
2224
+ T: 1 /*ScalarType.DOUBLE*/,
2225
+ },
2226
+ { no: 20, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
2227
+ {
2228
+ no: 21,
2229
+ name: 'freeze_duration_seconds',
2230
+ kind: 'scalar',
2231
+ T: 1 /*ScalarType.DOUBLE*/,
2232
+ },
2233
+ {
2234
+ no: 22,
2235
+ name: 'avg_decode_time_seconds',
2236
+ kind: 'scalar',
2237
+ T: 1 /*ScalarType.DOUBLE*/,
2238
+ },
2239
+ {
2240
+ no: 23,
2241
+ name: 'min_dimension_px',
2242
+ kind: 'scalar',
2243
+ T: 13 /*ScalarType.UINT32*/,
2244
+ },
2245
+ ]);
2246
+ }
2247
+ }
2248
+ /**
2249
+ * @generated MessageType for protobuf message stream.video.sfu.models.InboundRtp
2250
+ */
2251
+ const InboundRtp = new InboundRtp$Type();
2252
+ // @generated message type with reflection information, may provide speed optimized methods
2253
+ class OutboundRtp$Type extends MessageType {
2254
+ constructor() {
2255
+ super('stream.video.sfu.models.OutboundRtp', [
2256
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2257
+ { no: 10, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
2258
+ {
2259
+ no: 11,
2260
+ name: 'avg_encode_time_seconds',
2261
+ kind: 'scalar',
2262
+ T: 1 /*ScalarType.DOUBLE*/,
2263
+ },
2264
+ {
2265
+ no: 12,
2266
+ name: 'bitrate_bps',
2267
+ kind: 'scalar',
2268
+ T: 1 /*ScalarType.DOUBLE*/,
2269
+ },
2270
+ {
2271
+ no: 13,
2272
+ name: 'min_dimension_px',
2273
+ kind: 'scalar',
2274
+ T: 13 /*ScalarType.UINT32*/,
2275
+ },
2276
+ ]);
2277
+ }
2278
+ }
2279
+ /**
2280
+ * @generated MessageType for protobuf message stream.video.sfu.models.OutboundRtp
2281
+ */
2282
+ const OutboundRtp = new OutboundRtp$Type();
2283
+ // @generated message type with reflection information, may provide speed optimized methods
2284
+ class RemoteInboundRtp$Type extends MessageType {
2285
+ constructor() {
2286
+ super('stream.video.sfu.models.RemoteInboundRtp', [
2287
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2288
+ {
2289
+ no: 2,
2290
+ name: 'jitter_seconds',
2291
+ kind: 'scalar',
2292
+ T: 1 /*ScalarType.DOUBLE*/,
2293
+ },
2294
+ {
2295
+ no: 3,
2296
+ name: 'round_trip_time_s',
2297
+ kind: 'scalar',
2298
+ T: 1 /*ScalarType.DOUBLE*/,
2299
+ },
2300
+ ]);
2301
+ }
2302
+ }
2303
+ /**
2304
+ * @generated MessageType for protobuf message stream.video.sfu.models.RemoteInboundRtp
2305
+ */
2306
+ const RemoteInboundRtp = new RemoteInboundRtp$Type();
2307
+ // @generated message type with reflection information, may provide speed optimized methods
2308
+ class RemoteOutboundRtp$Type extends MessageType {
2309
+ constructor() {
2310
+ super('stream.video.sfu.models.RemoteOutboundRtp', [
2311
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2312
+ {
2313
+ no: 2,
2314
+ name: 'jitter_seconds',
2315
+ kind: 'scalar',
2316
+ T: 1 /*ScalarType.DOUBLE*/,
2317
+ },
2318
+ {
2319
+ no: 3,
2320
+ name: 'round_trip_time_s',
2321
+ kind: 'scalar',
2322
+ T: 1 /*ScalarType.DOUBLE*/,
2323
+ },
2324
+ ]);
2325
+ }
2326
+ }
2327
+ /**
2328
+ * @generated MessageType for protobuf message stream.video.sfu.models.RemoteOutboundRtp
2329
+ */
2330
+ const RemoteOutboundRtp = new RemoteOutboundRtp$Type();
2138
2331
 
2139
2332
  var models = /*#__PURE__*/Object.freeze({
2140
2333
  __proto__: null,
@@ -2159,8 +2352,10 @@ var models = /*#__PURE__*/Object.freeze({
2159
2352
  get ErrorCode () { return ErrorCode; },
2160
2353
  get GoAwayReason () { return GoAwayReason; },
2161
2354
  ICETrickle: ICETrickle$1,
2355
+ InboundRtp: InboundRtp,
2162
2356
  InputDevices: InputDevices,
2163
2357
  OS: OS,
2358
+ OutboundRtp: OutboundRtp,
2164
2359
  Participant: Participant,
2165
2360
  ParticipantCount: ParticipantCount,
2166
2361
  get ParticipantSource () { return ParticipantSource; },
@@ -2169,6 +2364,9 @@ var models = /*#__PURE__*/Object.freeze({
2169
2364
  Pin: Pin,
2170
2365
  PublishOption: PublishOption,
2171
2366
  RTMPIngress: RTMPIngress,
2367
+ RemoteInboundRtp: RemoteInboundRtp,
2368
+ RemoteOutboundRtp: RemoteOutboundRtp,
2369
+ RtpBase: RtpBase,
2172
2370
  Sdk: Sdk,
2173
2371
  get SdkType () { return SdkType; },
2174
2372
  StreamQuality: StreamQuality,
@@ -2286,6 +2484,62 @@ class Telemetry$Type extends MessageType {
2286
2484
  */
2287
2485
  const Telemetry = new Telemetry$Type();
2288
2486
  // @generated message type with reflection information, may provide speed optimized methods
2487
+ class SendMetricsRequest$Type extends MessageType {
2488
+ constructor() {
2489
+ super('stream.video.sfu.signal.SendMetricsRequest', [
2490
+ { no: 1, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2491
+ {
2492
+ no: 2,
2493
+ name: 'unified_session_id',
2494
+ kind: 'scalar',
2495
+ T: 9 /*ScalarType.STRING*/,
2496
+ },
2497
+ {
2498
+ no: 3,
2499
+ name: 'inbounds',
2500
+ kind: 'message',
2501
+ repeat: 2 /*RepeatType.UNPACKED*/,
2502
+ T: () => InboundRtp,
2503
+ },
2504
+ {
2505
+ no: 4,
2506
+ name: 'outbounds',
2507
+ kind: 'message',
2508
+ repeat: 2 /*RepeatType.UNPACKED*/,
2509
+ T: () => OutboundRtp,
2510
+ },
2511
+ {
2512
+ no: 5,
2513
+ name: 'remote_inbounds',
2514
+ kind: 'message',
2515
+ repeat: 2 /*RepeatType.UNPACKED*/,
2516
+ T: () => RemoteInboundRtp,
2517
+ },
2518
+ {
2519
+ no: 6,
2520
+ name: 'remote_outbounds',
2521
+ kind: 'message',
2522
+ repeat: 2 /*RepeatType.UNPACKED*/,
2523
+ T: () => RemoteOutboundRtp,
2524
+ },
2525
+ ]);
2526
+ }
2527
+ }
2528
+ /**
2529
+ * @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsRequest
2530
+ */
2531
+ const SendMetricsRequest = new SendMetricsRequest$Type();
2532
+ // @generated message type with reflection information, may provide speed optimized methods
2533
+ class SendMetricsResponse$Type extends MessageType {
2534
+ constructor() {
2535
+ super('stream.video.sfu.signal.SendMetricsResponse', []);
2536
+ }
2537
+ }
2538
+ /**
2539
+ * @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsResponse
2540
+ */
2541
+ const SendMetricsResponse = new SendMetricsResponse$Type();
2542
+ // @generated message type with reflection information, may provide speed optimized methods
2289
2543
  class SendStatsRequest$Type extends MessageType {
2290
2544
  constructor() {
2291
2545
  super('stream.video.sfu.signal.SendStatsRequest', [
@@ -2559,6 +2813,12 @@ class SendAnswerRequest$Type extends MessageType {
2559
2813
  },
2560
2814
  { no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2561
2815
  { no: 3, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2816
+ {
2817
+ no: 4,
2818
+ name: 'negotiation_id',
2819
+ kind: 'scalar',
2820
+ T: 13 /*ScalarType.UINT32*/,
2821
+ },
2562
2822
  ]);
2563
2823
  }
2564
2824
  }
@@ -2666,6 +2926,12 @@ const SignalServer = new ServiceType('stream.video.sfu.signal.SignalServer', [
2666
2926
  I: SendStatsRequest,
2667
2927
  O: SendStatsResponse,
2668
2928
  },
2929
+ {
2930
+ name: 'SendMetrics',
2931
+ options: {},
2932
+ I: SendMetricsRequest,
2933
+ O: SendMetricsResponse,
2934
+ },
2669
2935
  {
2670
2936
  name: 'StartNoiseCancellation',
2671
2937
  options: {},
@@ -3346,6 +3612,12 @@ class SubscriberOffer$Type extends MessageType {
3346
3612
  super('stream.video.sfu.event.SubscriberOffer', [
3347
3613
  { no: 1, name: 'ice_restart', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
3348
3614
  { no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
3615
+ {
3616
+ no: 3,
3617
+ name: 'negotiation_id',
3618
+ kind: 'scalar',
3619
+ T: 13 /*ScalarType.UINT32*/,
3620
+ },
3349
3621
  ]);
3350
3622
  }
3351
3623
  }
@@ -3804,18 +4076,25 @@ class SignalServerClient {
3804
4076
  const method = this.methods[6], opt = this._transport.mergeOptions(options);
3805
4077
  return stackIntercept('unary', this._transport, method, opt, input);
3806
4078
  }
4079
+ /**
4080
+ * @generated from protobuf rpc: SendMetrics(stream.video.sfu.signal.SendMetricsRequest) returns (stream.video.sfu.signal.SendMetricsResponse);
4081
+ */
4082
+ sendMetrics(input, options) {
4083
+ const method = this.methods[7], opt = this._transport.mergeOptions(options);
4084
+ return stackIntercept('unary', this._transport, method, opt, input);
4085
+ }
3807
4086
  /**
3808
4087
  * @generated from protobuf rpc: StartNoiseCancellation(stream.video.sfu.signal.StartNoiseCancellationRequest) returns (stream.video.sfu.signal.StartNoiseCancellationResponse);
3809
4088
  */
3810
4089
  startNoiseCancellation(input, options) {
3811
- const method = this.methods[7], opt = this._transport.mergeOptions(options);
4090
+ const method = this.methods[8], opt = this._transport.mergeOptions(options);
3812
4091
  return stackIntercept('unary', this._transport, method, opt, input);
3813
4092
  }
3814
4093
  /**
3815
4094
  * @generated from protobuf rpc: StopNoiseCancellation(stream.video.sfu.signal.StopNoiseCancellationRequest) returns (stream.video.sfu.signal.StopNoiseCancellationResponse);
3816
4095
  */
3817
4096
  stopNoiseCancellation(input, options) {
3818
- const method = this.methods[8], opt = this._transport.mergeOptions(options);
4097
+ const method = this.methods[9], opt = this._transport.mergeOptions(options);
3819
4098
  return stackIntercept('unary', this._transport, method, opt, input);
3820
4099
  }
3821
4100
  }
@@ -4995,6 +5274,16 @@ const hasScreenShareAudio = (p) => p.publishedTracks.includes(TrackType.SCREEN_S
4995
5274
  * @param p the participant.
4996
5275
  */
4997
5276
  const isPinned = (p) => !!p.pin && (p.pin.isLocalPin || p.pin.pinnedAt > 0);
5277
+ /**
5278
+ * Check if a participant has a track that is currently interrupted: the
5279
+ * participant intends to publish it (it is in `publishedTracks`) but no
5280
+ * media is flowing right now (it is in `interruptedTracks`).
5281
+ *
5282
+ * @param p the participant to check.
5283
+ * @param trackType the track type to check.
5284
+ */
5285
+ const hasInterruptedTrack = (p, trackType) => !!p.interruptedTracks?.includes(trackType) &&
5286
+ p.publishedTracks.includes(trackType);
4998
5287
  /**
4999
5288
  * Check if a participant has a paused track of the specified type.
5000
5289
  *
@@ -6360,7 +6649,7 @@ const getSdkVersion = (sdk) => {
6360
6649
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6361
6650
  };
6362
6651
 
6363
- const version = "1.51.0";
6652
+ const version = "1.52.1-beta.0";
6364
6653
  const [major, minor, patch] = version.split('.');
6365
6654
  let sdkInfo = {
6366
6655
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -6458,6 +6747,7 @@ const getClientDetails = async () => {
6458
6747
  sdk: sdkInfo,
6459
6748
  os: osInfo,
6460
6749
  device: deviceInfo,
6750
+ webrtcVersion: webRtcInfo?.version || '',
6461
6751
  };
6462
6752
  }
6463
6753
  // @ts-expect-error - userAgentData is not yet in the TS types
@@ -6485,11 +6775,12 @@ const getClientDetails = async () => {
6485
6775
  // Eliminates the generic "Chromium" name and "Not)A_Brand" name from the list.
6486
6776
  // https://wicg.github.io/ua-client-hints/#create-arbitrary-brands-section
6487
6777
  const uaBrowser = userAgentData?.fullVersionList?.find((v) => !v.brand.includes('Chromium') && !v.brand.match(/[()\-./:;=?_]/g));
6778
+ const browserVersion = uaBrowser?.version || browser.version || '';
6488
6779
  return {
6489
6780
  sdk: sdkInfo,
6490
6781
  browser: {
6491
6782
  name: uaBrowser?.brand || browser.name || navigator.userAgent,
6492
- version: uaBrowser?.version || browser.version || '',
6783
+ version: browserVersion,
6493
6784
  },
6494
6785
  os: {
6495
6786
  name: userAgentData?.platform || os.name || '',
@@ -6502,6 +6793,7 @@ const getClientDetails = async () => {
6502
6793
  .join(' '),
6503
6794
  version: '',
6504
6795
  },
6796
+ webrtcVersion: webRtcInfo?.version || '',
6505
6797
  };
6506
6798
  };
6507
6799
 
@@ -7165,7 +7457,7 @@ class StatsTracer {
7165
7457
  /**
7166
7458
  * Creates a new StatsTracer instance.
7167
7459
  */
7168
- constructor(pc, peerType, trackIdToTrackType) {
7460
+ constructor(pc, peerType, trackIdToTrackType, statsTimestampDriftThresholdMs = 0) {
7169
7461
  this.previousStats = {};
7170
7462
  this.frameTimeHistory = [];
7171
7463
  this.fpsHistory = [];
@@ -7179,7 +7471,7 @@ class StatsTracer {
7179
7471
  */
7180
7472
  this.get = async () => {
7181
7473
  const stats = await this.pc.getStats();
7182
- const currentStats = toObject(stats);
7474
+ const currentStats = toObjectWithCorrectedTimestamp(stats, Date.now(), this.driftThresholdMs);
7183
7475
  const performanceStats = this.withOverrides(this.peerType === PeerType.SUBSCRIBER
7184
7476
  ? this.getDecodeStats(currentStats)
7185
7477
  : this.getEncodeStats(currentStats));
@@ -7298,17 +7590,28 @@ class StatsTracer {
7298
7590
  this.pc = pc;
7299
7591
  this.peerType = peerType;
7300
7592
  this.trackIdToTrackType = trackIdToTrackType;
7593
+ this.driftThresholdMs = statsTimestampDriftThresholdMs;
7301
7594
  }
7302
7595
  }
7303
7596
  /**
7304
- * Convert the stat report to an object.
7597
+ * Convert the stat report to an object, correcting clock drift along the way.
7598
+ * Entries whose `timestamp` differs from `wallNow` by more than `thresholdMs`
7599
+ * are replaced with a clone whose `timestamp` is set to `wallNow`. The platform
7600
+ * clock backing `DOMHighResTimeStamp` can desynchronise from `Date.now()` after
7601
+ * system sleep or clock-jump events (notably on Electron/Chromium), which
7602
+ * corrupts the delta-compressed stats payload. A non-positive `thresholdMs`
7603
+ * disables correction.
7305
7604
  *
7306
7605
  * @param report the stat report to convert.
7606
+ * @param wallNow current wall-clock time used as the drift reference.
7607
+ * @param thresholdMs maximum tolerated drift in milliseconds.
7307
7608
  */
7308
- const toObject = (report) => {
7609
+ const toObjectWithCorrectedTimestamp = (report, wallNow, thresholdMs) => {
7309
7610
  const obj = {};
7611
+ const correct = thresholdMs > 0;
7310
7612
  report.forEach((v, k) => {
7311
- obj[k] = v;
7613
+ const drift = Math.abs(v.timestamp - wallNow);
7614
+ obj[k] = correct && drift > thresholdMs ? { ...v, timestamp: wallNow } : v;
7312
7615
  });
7313
7616
  return obj;
7314
7617
  };
@@ -7440,7 +7743,7 @@ class BasePeerConnection {
7440
7743
  /**
7441
7744
  * Constructs a new `BasePeerConnection` instance.
7442
7745
  */
7443
- constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, }) {
7746
+ constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, statsTimestampDriftThresholdMs = 0, }) {
7444
7747
  this.iceHasEverConnected = false;
7445
7748
  this.isIceRestarting = false;
7446
7749
  this.isDisposed = false;
@@ -7734,7 +8037,7 @@ class BasePeerConnection {
7734
8037
  this.onIceConnected = onIceConnected;
7735
8038
  this.logger = videoLoggerSystem.getLogger(peerType === PeerType.SUBSCRIBER ? 'Subscriber' : 'Publisher', { tags: [tag] });
7736
8039
  this.pc = this.createPeerConnection(connectionConfig);
7737
- this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType);
8040
+ this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType, statsTimestampDriftThresholdMs);
7738
8041
  if (enableTracing) {
7739
8042
  this.tracer = new Tracer(`${tag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}`);
7740
8043
  this.tracer.trace('create', {
@@ -8114,7 +8417,7 @@ class Publisher extends BasePeerConnection {
8114
8417
  /**
8115
8418
  * Constructs a new `Publisher` instance.
8116
8419
  */
8117
- constructor(baseOptions, publishOptions) {
8420
+ constructor(baseOptions, publishOptions, opts = {}) {
8118
8421
  super(PeerType.PUBLISHER_UNSPECIFIED, baseOptions);
8119
8422
  this.transceiverCache = new TransceiverCache();
8120
8423
  this.clonedTracks = new Set();
@@ -8535,6 +8838,7 @@ class Publisher extends BasePeerConnection {
8535
8838
  muted: !isTrackLive,
8536
8839
  codec: publishOption.codec,
8537
8840
  publishOptionId: publishOption.id,
8841
+ selfSubAudioVideo: this.selfSubEnabled,
8538
8842
  };
8539
8843
  };
8540
8844
  this.cloneTrack = (track) => {
@@ -8615,6 +8919,7 @@ class Publisher extends BasePeerConnection {
8615
8919
  });
8616
8920
  };
8617
8921
  this.publishOptions = publishOptions;
8922
+ this.selfSubEnabled = opts.selfSubEnabled ?? false;
8618
8923
  this.on('iceRestart', (iceRestart) => {
8619
8924
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
8620
8925
  return;
@@ -8696,6 +9001,13 @@ class Subscriber extends BasePeerConnection {
8696
9001
  */
8697
9002
  constructor(opts) {
8698
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();
8699
9011
  /**
8700
9012
  * Restarts the ICE connection and renegotiates with the SFU.
8701
9013
  */
@@ -8730,6 +9042,7 @@ class Subscriber extends BasePeerConnection {
8730
9042
  // example: `e3f6aaf8-b03d-4911-be36-83f47d37a76a:TRACK_TYPE_VIDEO`
8731
9043
  const [trackId, rawTrackType] = primaryStream.id.split(':');
8732
9044
  const participantToUpdate = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
9045
+ const isSelfSub = !!participantToUpdate?.isLocalParticipant;
8733
9046
  this.logger.debug(`[onTrack]: Got remote ${rawTrackType} track for userId: ${participantToUpdate?.userId}`, track.id, track);
8734
9047
  const trackType = toTrackType(rawTrackType);
8735
9048
  if (!trackType) {
@@ -8753,6 +9066,9 @@ class Subscriber extends BasePeerConnection {
8753
9066
  this.setRemoteTrackInterrupted(trackId, trackType, true);
8754
9067
  }
8755
9068
  this.trackIdToTrackType.set(track.id, trackType);
9069
+ if (isSelfSub) {
9070
+ this.trackedStreams.add(primaryStream);
9071
+ }
8756
9072
  if (!participantToUpdate) {
8757
9073
  this.logger.warn(`[onTrack]: Received track for unknown participant: ${trackId}`, e);
8758
9074
  this.state.registerOrphanedTrack({
@@ -8768,6 +9084,12 @@ class Subscriber extends BasePeerConnection {
8768
9084
  this.logger.error(`Unknown track type: ${rawTrackType}`);
8769
9085
  return;
8770
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
+ }
8771
9093
  // get the previous stream to dispose it later
8772
9094
  // usually this happens during migration, when the stream is replaced
8773
9095
  // with a new one but the old one is still in the state
@@ -8776,8 +9098,12 @@ class Subscriber extends BasePeerConnection {
8776
9098
  this.state.updateParticipant(participantToUpdate.sessionId, {
8777
9099
  [streamKindProp]: primaryStream,
8778
9100
  });
8779
- // now, dispose the previous stream if it exists
8780
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
+ }
8781
9107
  this.logger.info(`[onTrack]: Cleaning up previous remote ${track.kind} tracks for userId: ${participantToUpdate.userId}`);
8782
9108
  previousStream.getTracks().forEach((t) => {
8783
9109
  t.stop();
@@ -8820,6 +9146,7 @@ class Subscriber extends BasePeerConnection {
8820
9146
  await this.sfuClient.sendAnswer({
8821
9147
  peerType: PeerType.SUBSCRIBER,
8822
9148
  sdp: answer.sdp || '',
9149
+ negotiationId: subscriberOffer.negotiationId,
8823
9150
  });
8824
9151
  this.isIceRestarting = false;
8825
9152
  };
@@ -13534,6 +13861,7 @@ class Call {
13534
13861
  // maintain the order of publishing tracks to restore them after a reconnection
13535
13862
  // it shouldn't contain duplicates
13536
13863
  this.trackPublishOrder = [];
13864
+ this.selfSubEnabled = false;
13537
13865
  this.hasJoinedOnce = false;
13538
13866
  this.deviceSettingsAppliedOnce = false;
13539
13867
  this.initialized = false;
@@ -13878,6 +14206,30 @@ class Call {
13878
14206
  await Promise.all(stopOnLeavePromises);
13879
14207
  });
13880
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
+ };
13881
14233
  /**
13882
14234
  * Update from the call response from the "call.ring" event
13883
14235
  * @internal
@@ -14024,7 +14376,7 @@ class Call {
14024
14376
  *
14025
14377
  * @returns a promise which resolves once the call join-flow has finished.
14026
14378
  */
14027
- this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, ...data } = {}) => {
14379
+ this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, selfSubEnabled = false, ...data } = {}) => {
14028
14380
  const callingState = this.state.callingState;
14029
14381
  if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
14030
14382
  throw new Error(`Illegal State: call.join() shall be called only once`);
@@ -14032,6 +14384,9 @@ class Call {
14032
14384
  if (data?.ring) {
14033
14385
  this.ringingSubject.next(true);
14034
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;
14035
14390
  const callingX = globalThis.streamRNVideoSDK?.callingX;
14036
14391
  if (callingX) {
14037
14392
  // for Android/iOS, we need to start the call in the callingx library as soon as possible
@@ -14365,7 +14720,7 @@ class Call {
14365
14720
  */
14366
14721
  this.initPublisherAndSubscriber = async (opts) => {
14367
14722
  const { sfuClient, connectionConfig, clientDetails, statsOptions, publishOptions, closePreviousInstances, unifiedSessionId, } = opts;
14368
- const { enable_rtc_stats: enableTracing } = statsOptions;
14723
+ const { enable_rtc_stats: enableTracing, reporting_interval_ms: reportingIntervalMs, } = statsOptions;
14369
14724
  if (closePreviousInstances && this.subscriber) {
14370
14725
  await this.subscriber.dispose();
14371
14726
  }
@@ -14376,6 +14731,7 @@ class Call {
14376
14731
  connectionConfig,
14377
14732
  tag: sfuClient.tag,
14378
14733
  enableTracing,
14734
+ statsTimestampDriftThresholdMs: reportingIntervalMs / 2,
14379
14735
  clientPublishOptions: this.clientPublishOptions,
14380
14736
  onReconnectionNeeded: (kind, reason, peerType) => {
14381
14737
  this.reconnect(kind, reason).catch((err) => {
@@ -14398,7 +14754,9 @@ class Call {
14398
14754
  if (closePreviousInstances && this.publisher) {
14399
14755
  await this.publisher.dispose();
14400
14756
  }
14401
- this.publisher = new Publisher(basePeerConnectionOptions, publishOptions);
14757
+ this.publisher = new Publisher(basePeerConnectionOptions, publishOptions, {
14758
+ selfSubEnabled: this.selfSubEnabled,
14759
+ });
14402
14760
  }
14403
14761
  this.statsReporter?.stop();
14404
14762
  if (this.statsReportingIntervalInMs > 0) {
@@ -15828,6 +16186,12 @@ class Call {
15828
16186
  get currentUserId() {
15829
16187
  return this.clientStore.connectedUser?.id;
15830
16188
  }
16189
+ /**
16190
+ * A flag indicating whether self-subscription is enabled for the call.
16191
+ */
16192
+ get isSelfSubEnabled() {
16193
+ return this.selfSubEnabled;
16194
+ }
15831
16195
  /**
15832
16196
  * A flag indicating whether the call was created by the current user.
15833
16197
  */
@@ -17017,7 +17381,7 @@ class StreamClient {
17017
17381
  this.getUserAgent = () => {
17018
17382
  if (!this.cachedUserAgent) {
17019
17383
  const { clientAppIdentifier = {} } = this.options;
17020
- const { sdkName = 'js', sdkVersion = "1.51.0", ...extras } = clientAppIdentifier;
17384
+ const { sdkName = 'js', sdkVersion = "1.52.1-beta.0", ...extras } = clientAppIdentifier;
17021
17385
  this.cachedUserAgent = [
17022
17386
  `stream-video-${sdkName}-v${sdkVersion}`,
17023
17387
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
@@ -17653,5 +18017,5 @@ const humanize = (n) => {
17653
18017
  return String(n);
17654
18018
  };
17655
18019
 
17656
- export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RawRecordingSettingsRequestModeEnum, RawRecordingSettingsResponseModeEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, SfuJoinError, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio$1 as hasAudio, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, humanize, isPinned, livestreamOrAudioRoomSortPreset, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, videoLoggerSystem, withParticipantSource };
18020
+ export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RawRecordingSettingsRequestModeEnum, RawRecordingSettingsResponseModeEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, SfuJoinError, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio$1 as hasAudio, hasInterruptedTrack, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, humanize, isPinned, livestreamOrAudioRoomSortPreset, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, videoLoggerSystem, withParticipantSource };
17657
18021
  //# sourceMappingURL=index.es.js.map