@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
@@ -507,6 +507,7 @@ class ErrorFromResponse extends Error {
507
507
  }
508
508
  }
509
509
 
510
+ /* eslint-disable */
510
511
  // @generated by protobuf-ts 2.10.0 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
511
512
  // @generated from protobuf file "google/protobuf/struct.proto" (package "google.protobuf", syntax proto3)
512
513
  // tslint:disable
@@ -772,6 +773,7 @@ class ListValue$Type extends MessageType {
772
773
  */
773
774
  const ListValue = new ListValue$Type();
774
775
 
776
+ /* eslint-disable */
775
777
  // @generated by protobuf-ts 2.10.0 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
776
778
  // @generated from protobuf file "google/protobuf/timestamp.proto" (package "google.protobuf", syntax proto3)
777
779
  // tslint:disable
@@ -1186,6 +1188,14 @@ var SdkType;
1186
1188
  * @generated from protobuf enum value: SDK_TYPE_PLAIN_JAVASCRIPT = 9;
1187
1189
  */
1188
1190
  SdkType[SdkType["PLAIN_JAVASCRIPT"] = 9] = "PLAIN_JAVASCRIPT";
1191
+ /**
1192
+ * @generated from protobuf enum value: SDK_TYPE_PYTHON = 10;
1193
+ */
1194
+ SdkType[SdkType["PYTHON"] = 10] = "PYTHON";
1195
+ /**
1196
+ * @generated from protobuf enum value: SDK_TYPE_VISION_AGENTS = 11;
1197
+ */
1198
+ SdkType[SdkType["VISION_AGENTS"] = 11] = "VISION_AGENTS";
1189
1199
  })(SdkType || (SdkType = {}));
1190
1200
  /**
1191
1201
  * @generated from protobuf enum stream.video.sfu.models.TrackUnpublishReason
@@ -1397,6 +1407,12 @@ var ClientCapability;
1397
1407
  * @generated from protobuf enum value: CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE = 1;
1398
1408
  */
1399
1409
  ClientCapability[ClientCapability["SUBSCRIBER_VIDEO_PAUSE"] = 1] = "SUBSCRIBER_VIDEO_PAUSE";
1410
+ /**
1411
+ * Instructs SFU that stats will be sent to the coordinator
1412
+ *
1413
+ * @generated from protobuf enum value: CLIENT_CAPABILITY_COORDINATOR_STATS = 2;
1414
+ */
1415
+ ClientCapability[ClientCapability["COORDINATOR_STATS"] = 2] = "COORDINATOR_STATS";
1400
1416
  })(ClientCapability || (ClientCapability = {}));
1401
1417
  /**
1402
1418
  * DegradationPreference represents the RTCDegradationPreference from WebRTC.
@@ -1824,6 +1840,12 @@ class TrackInfo$Type extends MessageType {
1824
1840
  kind: 'scalar',
1825
1841
  T: 5 /*ScalarType.INT32*/,
1826
1842
  },
1843
+ {
1844
+ no: 13,
1845
+ name: 'self_sub_audio_video',
1846
+ kind: 'scalar',
1847
+ T: 8 /*ScalarType.BOOL*/,
1848
+ },
1827
1849
  ]);
1828
1850
  }
1829
1851
  }
@@ -1862,6 +1884,12 @@ class ClientDetails$Type extends MessageType {
1862
1884
  { no: 2, name: 'os', kind: 'message', T: () => OS },
1863
1885
  { no: 3, name: 'browser', kind: 'message', T: () => Browser },
1864
1886
  { no: 4, name: 'device', kind: 'message', T: () => Device },
1887
+ {
1888
+ no: 5,
1889
+ name: 'webrtc_version',
1890
+ kind: 'scalar',
1891
+ T: 9 /*ScalarType.STRING*/,
1892
+ },
1865
1893
  ]);
1866
1894
  }
1867
1895
  }
@@ -2134,6 +2162,171 @@ class PerformanceStats$Type extends MessageType {
2134
2162
  * @generated MessageType for protobuf message stream.video.sfu.models.PerformanceStats
2135
2163
  */
2136
2164
  const PerformanceStats = new PerformanceStats$Type();
2165
+ // @generated message type with reflection information, may provide speed optimized methods
2166
+ class RtpBase$Type extends MessageType {
2167
+ constructor() {
2168
+ super('stream.video.sfu.models.RtpBase', [
2169
+ { no: 1, name: 'ssrc', kind: 'scalar', T: 13 /*ScalarType.UINT32*/ },
2170
+ { no: 2, name: 'kind', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2171
+ {
2172
+ no: 3,
2173
+ name: 'timestamp_ms',
2174
+ kind: 'scalar',
2175
+ T: 1 /*ScalarType.DOUBLE*/,
2176
+ },
2177
+ ]);
2178
+ }
2179
+ }
2180
+ /**
2181
+ * @generated MessageType for protobuf message stream.video.sfu.models.RtpBase
2182
+ */
2183
+ const RtpBase = new RtpBase$Type();
2184
+ // @generated message type with reflection information, may provide speed optimized methods
2185
+ class InboundRtp$Type extends MessageType {
2186
+ constructor() {
2187
+ super('stream.video.sfu.models.InboundRtp', [
2188
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2189
+ {
2190
+ no: 2,
2191
+ name: 'jitter_seconds',
2192
+ kind: 'scalar',
2193
+ T: 1 /*ScalarType.DOUBLE*/,
2194
+ },
2195
+ {
2196
+ no: 3,
2197
+ name: 'packets_received',
2198
+ kind: 'scalar',
2199
+ T: 4 /*ScalarType.UINT64*/,
2200
+ },
2201
+ {
2202
+ no: 4,
2203
+ name: 'packets_lost',
2204
+ kind: 'scalar',
2205
+ T: 4 /*ScalarType.UINT64*/,
2206
+ },
2207
+ {
2208
+ no: 5,
2209
+ name: 'packet_loss_percent',
2210
+ kind: 'scalar',
2211
+ T: 1 /*ScalarType.DOUBLE*/,
2212
+ },
2213
+ {
2214
+ no: 10,
2215
+ name: 'concealment_events',
2216
+ kind: 'scalar',
2217
+ T: 13 /*ScalarType.UINT32*/,
2218
+ },
2219
+ {
2220
+ no: 11,
2221
+ name: 'concealment_percent',
2222
+ kind: 'scalar',
2223
+ T: 1 /*ScalarType.DOUBLE*/,
2224
+ },
2225
+ { no: 20, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
2226
+ {
2227
+ no: 21,
2228
+ name: 'freeze_duration_seconds',
2229
+ kind: 'scalar',
2230
+ T: 1 /*ScalarType.DOUBLE*/,
2231
+ },
2232
+ {
2233
+ no: 22,
2234
+ name: 'avg_decode_time_seconds',
2235
+ kind: 'scalar',
2236
+ T: 1 /*ScalarType.DOUBLE*/,
2237
+ },
2238
+ {
2239
+ no: 23,
2240
+ name: 'min_dimension_px',
2241
+ kind: 'scalar',
2242
+ T: 13 /*ScalarType.UINT32*/,
2243
+ },
2244
+ ]);
2245
+ }
2246
+ }
2247
+ /**
2248
+ * @generated MessageType for protobuf message stream.video.sfu.models.InboundRtp
2249
+ */
2250
+ const InboundRtp = new InboundRtp$Type();
2251
+ // @generated message type with reflection information, may provide speed optimized methods
2252
+ class OutboundRtp$Type extends MessageType {
2253
+ constructor() {
2254
+ super('stream.video.sfu.models.OutboundRtp', [
2255
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2256
+ { no: 10, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
2257
+ {
2258
+ no: 11,
2259
+ name: 'avg_encode_time_seconds',
2260
+ kind: 'scalar',
2261
+ T: 1 /*ScalarType.DOUBLE*/,
2262
+ },
2263
+ {
2264
+ no: 12,
2265
+ name: 'bitrate_bps',
2266
+ kind: 'scalar',
2267
+ T: 1 /*ScalarType.DOUBLE*/,
2268
+ },
2269
+ {
2270
+ no: 13,
2271
+ name: 'min_dimension_px',
2272
+ kind: 'scalar',
2273
+ T: 13 /*ScalarType.UINT32*/,
2274
+ },
2275
+ ]);
2276
+ }
2277
+ }
2278
+ /**
2279
+ * @generated MessageType for protobuf message stream.video.sfu.models.OutboundRtp
2280
+ */
2281
+ const OutboundRtp = new OutboundRtp$Type();
2282
+ // @generated message type with reflection information, may provide speed optimized methods
2283
+ class RemoteInboundRtp$Type extends MessageType {
2284
+ constructor() {
2285
+ super('stream.video.sfu.models.RemoteInboundRtp', [
2286
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2287
+ {
2288
+ no: 2,
2289
+ name: 'jitter_seconds',
2290
+ kind: 'scalar',
2291
+ T: 1 /*ScalarType.DOUBLE*/,
2292
+ },
2293
+ {
2294
+ no: 3,
2295
+ name: 'round_trip_time_s',
2296
+ kind: 'scalar',
2297
+ T: 1 /*ScalarType.DOUBLE*/,
2298
+ },
2299
+ ]);
2300
+ }
2301
+ }
2302
+ /**
2303
+ * @generated MessageType for protobuf message stream.video.sfu.models.RemoteInboundRtp
2304
+ */
2305
+ const RemoteInboundRtp = new RemoteInboundRtp$Type();
2306
+ // @generated message type with reflection information, may provide speed optimized methods
2307
+ class RemoteOutboundRtp$Type extends MessageType {
2308
+ constructor() {
2309
+ super('stream.video.sfu.models.RemoteOutboundRtp', [
2310
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2311
+ {
2312
+ no: 2,
2313
+ name: 'jitter_seconds',
2314
+ kind: 'scalar',
2315
+ T: 1 /*ScalarType.DOUBLE*/,
2316
+ },
2317
+ {
2318
+ no: 3,
2319
+ name: 'round_trip_time_s',
2320
+ kind: 'scalar',
2321
+ T: 1 /*ScalarType.DOUBLE*/,
2322
+ },
2323
+ ]);
2324
+ }
2325
+ }
2326
+ /**
2327
+ * @generated MessageType for protobuf message stream.video.sfu.models.RemoteOutboundRtp
2328
+ */
2329
+ const RemoteOutboundRtp = new RemoteOutboundRtp$Type();
2137
2330
 
2138
2331
  var models = /*#__PURE__*/Object.freeze({
2139
2332
  __proto__: null,
@@ -2158,8 +2351,10 @@ var models = /*#__PURE__*/Object.freeze({
2158
2351
  get ErrorCode () { return ErrorCode; },
2159
2352
  get GoAwayReason () { return GoAwayReason; },
2160
2353
  ICETrickle: ICETrickle$1,
2354
+ InboundRtp: InboundRtp,
2161
2355
  InputDevices: InputDevices,
2162
2356
  OS: OS,
2357
+ OutboundRtp: OutboundRtp,
2163
2358
  Participant: Participant,
2164
2359
  ParticipantCount: ParticipantCount,
2165
2360
  get ParticipantSource () { return ParticipantSource; },
@@ -2168,6 +2363,9 @@ var models = /*#__PURE__*/Object.freeze({
2168
2363
  Pin: Pin,
2169
2364
  PublishOption: PublishOption,
2170
2365
  RTMPIngress: RTMPIngress,
2366
+ RemoteInboundRtp: RemoteInboundRtp,
2367
+ RemoteOutboundRtp: RemoteOutboundRtp,
2368
+ RtpBase: RtpBase,
2171
2369
  Sdk: Sdk,
2172
2370
  get SdkType () { return SdkType; },
2173
2371
  StreamQuality: StreamQuality,
@@ -2285,6 +2483,62 @@ class Telemetry$Type extends MessageType {
2285
2483
  */
2286
2484
  const Telemetry = new Telemetry$Type();
2287
2485
  // @generated message type with reflection information, may provide speed optimized methods
2486
+ class SendMetricsRequest$Type extends MessageType {
2487
+ constructor() {
2488
+ super('stream.video.sfu.signal.SendMetricsRequest', [
2489
+ { no: 1, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2490
+ {
2491
+ no: 2,
2492
+ name: 'unified_session_id',
2493
+ kind: 'scalar',
2494
+ T: 9 /*ScalarType.STRING*/,
2495
+ },
2496
+ {
2497
+ no: 3,
2498
+ name: 'inbounds',
2499
+ kind: 'message',
2500
+ repeat: 2 /*RepeatType.UNPACKED*/,
2501
+ T: () => InboundRtp,
2502
+ },
2503
+ {
2504
+ no: 4,
2505
+ name: 'outbounds',
2506
+ kind: 'message',
2507
+ repeat: 2 /*RepeatType.UNPACKED*/,
2508
+ T: () => OutboundRtp,
2509
+ },
2510
+ {
2511
+ no: 5,
2512
+ name: 'remote_inbounds',
2513
+ kind: 'message',
2514
+ repeat: 2 /*RepeatType.UNPACKED*/,
2515
+ T: () => RemoteInboundRtp,
2516
+ },
2517
+ {
2518
+ no: 6,
2519
+ name: 'remote_outbounds',
2520
+ kind: 'message',
2521
+ repeat: 2 /*RepeatType.UNPACKED*/,
2522
+ T: () => RemoteOutboundRtp,
2523
+ },
2524
+ ]);
2525
+ }
2526
+ }
2527
+ /**
2528
+ * @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsRequest
2529
+ */
2530
+ const SendMetricsRequest = new SendMetricsRequest$Type();
2531
+ // @generated message type with reflection information, may provide speed optimized methods
2532
+ class SendMetricsResponse$Type extends MessageType {
2533
+ constructor() {
2534
+ super('stream.video.sfu.signal.SendMetricsResponse', []);
2535
+ }
2536
+ }
2537
+ /**
2538
+ * @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsResponse
2539
+ */
2540
+ const SendMetricsResponse = new SendMetricsResponse$Type();
2541
+ // @generated message type with reflection information, may provide speed optimized methods
2288
2542
  class SendStatsRequest$Type extends MessageType {
2289
2543
  constructor() {
2290
2544
  super('stream.video.sfu.signal.SendStatsRequest', [
@@ -2558,6 +2812,12 @@ class SendAnswerRequest$Type extends MessageType {
2558
2812
  },
2559
2813
  { no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2560
2814
  { no: 3, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2815
+ {
2816
+ no: 4,
2817
+ name: 'negotiation_id',
2818
+ kind: 'scalar',
2819
+ T: 13 /*ScalarType.UINT32*/,
2820
+ },
2561
2821
  ]);
2562
2822
  }
2563
2823
  }
@@ -2665,6 +2925,12 @@ const SignalServer = new ServiceType('stream.video.sfu.signal.SignalServer', [
2665
2925
  I: SendStatsRequest,
2666
2926
  O: SendStatsResponse,
2667
2927
  },
2928
+ {
2929
+ name: 'SendMetrics',
2930
+ options: {},
2931
+ I: SendMetricsRequest,
2932
+ O: SendMetricsResponse,
2933
+ },
2668
2934
  {
2669
2935
  name: 'StartNoiseCancellation',
2670
2936
  options: {},
@@ -3345,6 +3611,12 @@ class SubscriberOffer$Type extends MessageType {
3345
3611
  super('stream.video.sfu.event.SubscriberOffer', [
3346
3612
  { no: 1, name: 'ice_restart', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
3347
3613
  { no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
3614
+ {
3615
+ no: 3,
3616
+ name: 'negotiation_id',
3617
+ kind: 'scalar',
3618
+ T: 13 /*ScalarType.UINT32*/,
3619
+ },
3348
3620
  ]);
3349
3621
  }
3350
3622
  }
@@ -3803,18 +4075,25 @@ class SignalServerClient {
3803
4075
  const method = this.methods[6], opt = this._transport.mergeOptions(options);
3804
4076
  return stackIntercept('unary', this._transport, method, opt, input);
3805
4077
  }
4078
+ /**
4079
+ * @generated from protobuf rpc: SendMetrics(stream.video.sfu.signal.SendMetricsRequest) returns (stream.video.sfu.signal.SendMetricsResponse);
4080
+ */
4081
+ sendMetrics(input, options) {
4082
+ const method = this.methods[7], opt = this._transport.mergeOptions(options);
4083
+ return stackIntercept('unary', this._transport, method, opt, input);
4084
+ }
3806
4085
  /**
3807
4086
  * @generated from protobuf rpc: StartNoiseCancellation(stream.video.sfu.signal.StartNoiseCancellationRequest) returns (stream.video.sfu.signal.StartNoiseCancellationResponse);
3808
4087
  */
3809
4088
  startNoiseCancellation(input, options) {
3810
- const method = this.methods[7], opt = this._transport.mergeOptions(options);
4089
+ const method = this.methods[8], opt = this._transport.mergeOptions(options);
3811
4090
  return stackIntercept('unary', this._transport, method, opt, input);
3812
4091
  }
3813
4092
  /**
3814
4093
  * @generated from protobuf rpc: StopNoiseCancellation(stream.video.sfu.signal.StopNoiseCancellationRequest) returns (stream.video.sfu.signal.StopNoiseCancellationResponse);
3815
4094
  */
3816
4095
  stopNoiseCancellation(input, options) {
3817
- const method = this.methods[8], opt = this._transport.mergeOptions(options);
4096
+ const method = this.methods[9], opt = this._transport.mergeOptions(options);
3818
4097
  return stackIntercept('unary', this._transport, method, opt, input);
3819
4098
  }
3820
4099
  }
@@ -4994,6 +5273,16 @@ const hasScreenShareAudio = (p) => p.publishedTracks.includes(TrackType.SCREEN_S
4994
5273
  * @param p the participant.
4995
5274
  */
4996
5275
  const isPinned = (p) => !!p.pin && (p.pin.isLocalPin || p.pin.pinnedAt > 0);
5276
+ /**
5277
+ * Check if a participant has a track that is currently interrupted: the
5278
+ * participant intends to publish it (it is in `publishedTracks`) but no
5279
+ * media is flowing right now (it is in `interruptedTracks`).
5280
+ *
5281
+ * @param p the participant to check.
5282
+ * @param trackType the track type to check.
5283
+ */
5284
+ const hasInterruptedTrack = (p, trackType) => !!p.interruptedTracks?.includes(trackType) &&
5285
+ p.publishedTracks.includes(trackType);
4997
5286
  /**
4998
5287
  * Check if a participant has a paused track of the specified type.
4999
5288
  *
@@ -6359,7 +6648,7 @@ const getSdkVersion = (sdk) => {
6359
6648
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6360
6649
  };
6361
6650
 
6362
- const version = "1.51.0";
6651
+ const version = "1.52.1-beta.0";
6363
6652
  const [major, minor, patch] = version.split('.');
6364
6653
  let sdkInfo = {
6365
6654
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -6457,6 +6746,7 @@ const getClientDetails = async () => {
6457
6746
  sdk: sdkInfo,
6458
6747
  os: osInfo,
6459
6748
  device: deviceInfo,
6749
+ webrtcVersion: webRtcInfo?.version || '',
6460
6750
  };
6461
6751
  }
6462
6752
  // @ts-expect-error - userAgentData is not yet in the TS types
@@ -6484,11 +6774,12 @@ const getClientDetails = async () => {
6484
6774
  // Eliminates the generic "Chromium" name and "Not)A_Brand" name from the list.
6485
6775
  // https://wicg.github.io/ua-client-hints/#create-arbitrary-brands-section
6486
6776
  const uaBrowser = userAgentData?.fullVersionList?.find((v) => !v.brand.includes('Chromium') && !v.brand.match(/[()\-./:;=?_]/g));
6777
+ const browserVersion = uaBrowser?.version || browser.version || '';
6487
6778
  return {
6488
6779
  sdk: sdkInfo,
6489
6780
  browser: {
6490
6781
  name: uaBrowser?.brand || browser.name || navigator.userAgent,
6491
- version: uaBrowser?.version || browser.version || '',
6782
+ version: browserVersion,
6492
6783
  },
6493
6784
  os: {
6494
6785
  name: userAgentData?.platform || os.name || '',
@@ -6501,6 +6792,7 @@ const getClientDetails = async () => {
6501
6792
  .join(' '),
6502
6793
  version: '',
6503
6794
  },
6795
+ webrtcVersion: webRtcInfo?.version || '',
6504
6796
  };
6505
6797
  };
6506
6798
 
@@ -7164,7 +7456,7 @@ class StatsTracer {
7164
7456
  /**
7165
7457
  * Creates a new StatsTracer instance.
7166
7458
  */
7167
- constructor(pc, peerType, trackIdToTrackType) {
7459
+ constructor(pc, peerType, trackIdToTrackType, statsTimestampDriftThresholdMs = 0) {
7168
7460
  this.previousStats = {};
7169
7461
  this.frameTimeHistory = [];
7170
7462
  this.fpsHistory = [];
@@ -7178,7 +7470,7 @@ class StatsTracer {
7178
7470
  */
7179
7471
  this.get = async () => {
7180
7472
  const stats = await this.pc.getStats();
7181
- const currentStats = toObject(stats);
7473
+ const currentStats = toObjectWithCorrectedTimestamp(stats, Date.now(), this.driftThresholdMs);
7182
7474
  const performanceStats = this.withOverrides(this.peerType === PeerType.SUBSCRIBER
7183
7475
  ? this.getDecodeStats(currentStats)
7184
7476
  : this.getEncodeStats(currentStats));
@@ -7297,17 +7589,28 @@ class StatsTracer {
7297
7589
  this.pc = pc;
7298
7590
  this.peerType = peerType;
7299
7591
  this.trackIdToTrackType = trackIdToTrackType;
7592
+ this.driftThresholdMs = statsTimestampDriftThresholdMs;
7300
7593
  }
7301
7594
  }
7302
7595
  /**
7303
- * Convert the stat report to an object.
7596
+ * Convert the stat report to an object, correcting clock drift along the way.
7597
+ * Entries whose `timestamp` differs from `wallNow` by more than `thresholdMs`
7598
+ * are replaced with a clone whose `timestamp` is set to `wallNow`. The platform
7599
+ * clock backing `DOMHighResTimeStamp` can desynchronise from `Date.now()` after
7600
+ * system sleep or clock-jump events (notably on Electron/Chromium), which
7601
+ * corrupts the delta-compressed stats payload. A non-positive `thresholdMs`
7602
+ * disables correction.
7304
7603
  *
7305
7604
  * @param report the stat report to convert.
7605
+ * @param wallNow current wall-clock time used as the drift reference.
7606
+ * @param thresholdMs maximum tolerated drift in milliseconds.
7306
7607
  */
7307
- const toObject = (report) => {
7608
+ const toObjectWithCorrectedTimestamp = (report, wallNow, thresholdMs) => {
7308
7609
  const obj = {};
7610
+ const correct = thresholdMs > 0;
7309
7611
  report.forEach((v, k) => {
7310
- obj[k] = v;
7612
+ const drift = Math.abs(v.timestamp - wallNow);
7613
+ obj[k] = correct && drift > thresholdMs ? { ...v, timestamp: wallNow } : v;
7311
7614
  });
7312
7615
  return obj;
7313
7616
  };
@@ -7439,7 +7742,7 @@ class BasePeerConnection {
7439
7742
  /**
7440
7743
  * Constructs a new `BasePeerConnection` instance.
7441
7744
  */
7442
- constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, }) {
7745
+ constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, statsTimestampDriftThresholdMs = 0, }) {
7443
7746
  this.iceHasEverConnected = false;
7444
7747
  this.isIceRestarting = false;
7445
7748
  this.isDisposed = false;
@@ -7733,7 +8036,7 @@ class BasePeerConnection {
7733
8036
  this.onIceConnected = onIceConnected;
7734
8037
  this.logger = videoLoggerSystem.getLogger(peerType === PeerType.SUBSCRIBER ? 'Subscriber' : 'Publisher', { tags: [tag] });
7735
8038
  this.pc = this.createPeerConnection(connectionConfig);
7736
- this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType);
8039
+ this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType, statsTimestampDriftThresholdMs);
7737
8040
  if (enableTracing) {
7738
8041
  this.tracer = new Tracer(`${tag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}`);
7739
8042
  this.tracer.trace('create', {
@@ -8113,7 +8416,7 @@ class Publisher extends BasePeerConnection {
8113
8416
  /**
8114
8417
  * Constructs a new `Publisher` instance.
8115
8418
  */
8116
- constructor(baseOptions, publishOptions) {
8419
+ constructor(baseOptions, publishOptions, opts = {}) {
8117
8420
  super(PeerType.PUBLISHER_UNSPECIFIED, baseOptions);
8118
8421
  this.transceiverCache = new TransceiverCache();
8119
8422
  this.clonedTracks = new Set();
@@ -8534,6 +8837,7 @@ class Publisher extends BasePeerConnection {
8534
8837
  muted: !isTrackLive,
8535
8838
  codec: publishOption.codec,
8536
8839
  publishOptionId: publishOption.id,
8840
+ selfSubAudioVideo: this.selfSubEnabled,
8537
8841
  };
8538
8842
  };
8539
8843
  this.cloneTrack = (track) => {
@@ -8614,6 +8918,7 @@ class Publisher extends BasePeerConnection {
8614
8918
  });
8615
8919
  };
8616
8920
  this.publishOptions = publishOptions;
8921
+ this.selfSubEnabled = opts.selfSubEnabled ?? false;
8617
8922
  this.on('iceRestart', (iceRestart) => {
8618
8923
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
8619
8924
  return;
@@ -8695,6 +9000,13 @@ class Subscriber extends BasePeerConnection {
8695
9000
  */
8696
9001
  constructor(opts) {
8697
9002
  super(PeerType.SUBSCRIBER, opts);
9003
+ /**
9004
+ * Remote streams received from the SFU. For a self-sub case
9005
+ * we need to be able to distinguish between the local capture stream.
9006
+ * The map will never contain local streams so we can safely use it to
9007
+ * check if the stream is remote and dispose it when needed.
9008
+ */
9009
+ this.trackedStreams = new WeakSet();
8698
9010
  /**
8699
9011
  * Restarts the ICE connection and renegotiates with the SFU.
8700
9012
  */
@@ -8729,6 +9041,7 @@ class Subscriber extends BasePeerConnection {
8729
9041
  // example: `e3f6aaf8-b03d-4911-be36-83f47d37a76a:TRACK_TYPE_VIDEO`
8730
9042
  const [trackId, rawTrackType] = primaryStream.id.split(':');
8731
9043
  const participantToUpdate = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
9044
+ const isSelfSub = !!participantToUpdate?.isLocalParticipant;
8732
9045
  this.logger.debug(`[onTrack]: Got remote ${rawTrackType} track for userId: ${participantToUpdate?.userId}`, track.id, track);
8733
9046
  const trackType = toTrackType(rawTrackType);
8734
9047
  if (!trackType) {
@@ -8752,6 +9065,9 @@ class Subscriber extends BasePeerConnection {
8752
9065
  this.setRemoteTrackInterrupted(trackId, trackType, true);
8753
9066
  }
8754
9067
  this.trackIdToTrackType.set(track.id, trackType);
9068
+ if (isSelfSub) {
9069
+ this.trackedStreams.add(primaryStream);
9070
+ }
8755
9071
  if (!participantToUpdate) {
8756
9072
  this.logger.warn(`[onTrack]: Received track for unknown participant: ${trackId}`, e);
8757
9073
  this.state.registerOrphanedTrack({
@@ -8767,6 +9083,12 @@ class Subscriber extends BasePeerConnection {
8767
9083
  this.logger.error(`Unknown track type: ${rawTrackType}`);
8768
9084
  return;
8769
9085
  }
9086
+ // Self-sub loopback audio routes to the speaker by default, which
9087
+ // would echo the local user's voice. Default-mute here; consumers
9088
+ // (the loopback recording hook) re-enable explicitly when needed.
9089
+ if (isSelfSub && e.track.kind === 'audio') {
9090
+ e.track.enabled = false;
9091
+ }
8770
9092
  // get the previous stream to dispose it later
8771
9093
  // usually this happens during migration, when the stream is replaced
8772
9094
  // with a new one but the old one is still in the state
@@ -8775,8 +9097,12 @@ class Subscriber extends BasePeerConnection {
8775
9097
  this.state.updateParticipant(participantToUpdate.sessionId, {
8776
9098
  [streamKindProp]: primaryStream,
8777
9099
  });
8778
- // now, dispose the previous stream if it exists
8779
9100
  if (previousStream) {
9101
+ if (isSelfSub && !this.trackedStreams.has(previousStream)) {
9102
+ // this is the local capture stream, we don't want to dispose it
9103
+ this.logger.debug(`[onTrack]: Skipping cleanup of previous ${e.track.kind} stream for userId: ${participantToUpdate.userId} because it is not tracked`);
9104
+ return;
9105
+ }
8780
9106
  this.logger.info(`[onTrack]: Cleaning up previous remote ${track.kind} tracks for userId: ${participantToUpdate.userId}`);
8781
9107
  previousStream.getTracks().forEach((t) => {
8782
9108
  t.stop();
@@ -8819,6 +9145,7 @@ class Subscriber extends BasePeerConnection {
8819
9145
  await this.sfuClient.sendAnswer({
8820
9146
  peerType: PeerType.SUBSCRIBER,
8821
9147
  sdp: answer.sdp || '',
9148
+ negotiationId: subscriberOffer.negotiationId,
8822
9149
  });
8823
9150
  this.isIceRestarting = false;
8824
9151
  };
@@ -13533,6 +13860,7 @@ class Call {
13533
13860
  // maintain the order of publishing tracks to restore them after a reconnection
13534
13861
  // it shouldn't contain duplicates
13535
13862
  this.trackPublishOrder = [];
13863
+ this.selfSubEnabled = false;
13536
13864
  this.hasJoinedOnce = false;
13537
13865
  this.deviceSettingsAppliedOnce = false;
13538
13866
  this.initialized = false;
@@ -13877,6 +14205,30 @@ class Call {
13877
14205
  await Promise.all(stopOnLeavePromises);
13878
14206
  });
13879
14207
  };
14208
+ /**
14209
+ * The largest video publish dimension across the current publish options.
14210
+ *
14211
+ * @internal
14212
+ */
14213
+ this.getMaxVideoPublishDimension = () => {
14214
+ if (!this.currentPublishOptions)
14215
+ return undefined;
14216
+ let maxDimension;
14217
+ let maxArea = 0;
14218
+ for (const opt of this.currentPublishOptions) {
14219
+ if (opt.trackType !== TrackType.VIDEO)
14220
+ continue;
14221
+ const dim = opt.videoDimension;
14222
+ if (!dim || !dim.width || !dim.height)
14223
+ continue;
14224
+ const area = dim.width * dim.height;
14225
+ if (area > maxArea) {
14226
+ maxDimension = dim;
14227
+ maxArea = area;
14228
+ }
14229
+ }
14230
+ return maxDimension;
14231
+ };
13880
14232
  /**
13881
14233
  * Update from the call response from the "call.ring" event
13882
14234
  * @internal
@@ -14023,7 +14375,7 @@ class Call {
14023
14375
  *
14024
14376
  * @returns a promise which resolves once the call join-flow has finished.
14025
14377
  */
14026
- this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, ...data } = {}) => {
14378
+ this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, selfSubEnabled = false, ...data } = {}) => {
14027
14379
  const callingState = this.state.callingState;
14028
14380
  if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
14029
14381
  throw new Error(`Illegal State: call.join() shall be called only once`);
@@ -14031,6 +14383,9 @@ class Call {
14031
14383
  if (data?.ring) {
14032
14384
  this.ringingSubject.next(true);
14033
14385
  }
14386
+ // we need this to be set before the callingx.joinCall() is
14387
+ // called to avoid registering the test call in the CallKit/Telecom
14388
+ this.selfSubEnabled = selfSubEnabled;
14034
14389
  const callingX = globalThis.streamRNVideoSDK?.callingX;
14035
14390
  if (callingX) {
14036
14391
  // for Android/iOS, we need to start the call in the callingx library as soon as possible
@@ -14364,7 +14719,7 @@ class Call {
14364
14719
  */
14365
14720
  this.initPublisherAndSubscriber = async (opts) => {
14366
14721
  const { sfuClient, connectionConfig, clientDetails, statsOptions, publishOptions, closePreviousInstances, unifiedSessionId, } = opts;
14367
- const { enable_rtc_stats: enableTracing } = statsOptions;
14722
+ const { enable_rtc_stats: enableTracing, reporting_interval_ms: reportingIntervalMs, } = statsOptions;
14368
14723
  if (closePreviousInstances && this.subscriber) {
14369
14724
  await this.subscriber.dispose();
14370
14725
  }
@@ -14375,6 +14730,7 @@ class Call {
14375
14730
  connectionConfig,
14376
14731
  tag: sfuClient.tag,
14377
14732
  enableTracing,
14733
+ statsTimestampDriftThresholdMs: reportingIntervalMs / 2,
14378
14734
  clientPublishOptions: this.clientPublishOptions,
14379
14735
  onReconnectionNeeded: (kind, reason, peerType) => {
14380
14736
  this.reconnect(kind, reason).catch((err) => {
@@ -14397,7 +14753,9 @@ class Call {
14397
14753
  if (closePreviousInstances && this.publisher) {
14398
14754
  await this.publisher.dispose();
14399
14755
  }
14400
- this.publisher = new Publisher(basePeerConnectionOptions, publishOptions);
14756
+ this.publisher = new Publisher(basePeerConnectionOptions, publishOptions, {
14757
+ selfSubEnabled: this.selfSubEnabled,
14758
+ });
14401
14759
  }
14402
14760
  this.statsReporter?.stop();
14403
14761
  if (this.statsReportingIntervalInMs > 0) {
@@ -15827,6 +16185,12 @@ class Call {
15827
16185
  get currentUserId() {
15828
16186
  return this.clientStore.connectedUser?.id;
15829
16187
  }
16188
+ /**
16189
+ * A flag indicating whether self-subscription is enabled for the call.
16190
+ */
16191
+ get isSelfSubEnabled() {
16192
+ return this.selfSubEnabled;
16193
+ }
15830
16194
  /**
15831
16195
  * A flag indicating whether the call was created by the current user.
15832
16196
  */
@@ -17018,7 +17382,7 @@ class StreamClient {
17018
17382
  this.getUserAgent = () => {
17019
17383
  if (!this.cachedUserAgent) {
17020
17384
  const { clientAppIdentifier = {} } = this.options;
17021
- const { sdkName = 'js', sdkVersion = "1.51.0", ...extras } = clientAppIdentifier;
17385
+ const { sdkName = 'js', sdkVersion = "1.52.1-beta.0", ...extras } = clientAppIdentifier;
17022
17386
  this.cachedUserAgent = [
17023
17387
  `stream-video-${sdkName}-v${sdkVersion}`,
17024
17388
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
@@ -17654,5 +18018,5 @@ const humanize = (n) => {
17654
18018
  return String(n);
17655
18019
  };
17656
18020
 
17657
- 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 };
18021
+ 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 };
17658
18022
  //# sourceMappingURL=index.browser.es.js.map