@stream-io/video-client 1.51.0 → 1.52.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 (40) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.browser.es.js +311 -14
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +311 -13
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +311 -14
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/gen/video/sfu/event/events.d.ts +4 -0
  9. package/dist/src/gen/video/sfu/models/models.d.ts +204 -2
  10. package/dist/src/gen/video/sfu/signal_rpc/signal.client.d.ts +9 -1
  11. package/dist/src/gen/video/sfu/signal_rpc/signal.d.ts +67 -1
  12. package/dist/src/helpers/participantUtils.d.ts +10 -0
  13. package/dist/src/rtc/BasePeerConnection.d.ts +1 -1
  14. package/dist/src/rtc/types.d.ts +1 -0
  15. package/dist/src/stats/rtc/StatsTracer.d.ts +2 -1
  16. package/dist/src/stats/utils.d.ts +1 -0
  17. package/package.json +14 -14
  18. package/src/Call.ts +5 -1
  19. package/src/devices/__tests__/CameraManager.test.ts +3 -1
  20. package/src/devices/__tests__/DeviceManager.test.ts +3 -1
  21. package/src/devices/__tests__/MicrophoneManager.test.ts +3 -1
  22. package/src/devices/__tests__/MicrophoneManagerRN.test.ts +3 -1
  23. package/src/devices/__tests__/ScreenShareManager.test.ts +3 -1
  24. package/src/devices/__tests__/web-audio.mocks.ts +3 -1
  25. package/src/gen/video/sfu/event/events.ts +10 -0
  26. package/src/gen/video/sfu/models/models.ts +338 -0
  27. package/src/gen/video/sfu/signal_rpc/signal.client.ts +28 -2
  28. package/src/gen/video/sfu/signal_rpc/signal.ts +121 -15
  29. package/src/helpers/__tests__/DynascaleManager.test.ts +8 -7
  30. package/src/helpers/__tests__/browsers.test.ts +4 -4
  31. package/src/helpers/__tests__/participantUtils.test.ts +47 -0
  32. package/src/helpers/client-details.ts +4 -1
  33. package/src/helpers/participantUtils.ts +15 -0
  34. package/src/rtc/BasePeerConnection.ts +7 -1
  35. package/src/rtc/Subscriber.ts +1 -0
  36. package/src/rtc/__tests__/Subscriber.test.ts +5 -1
  37. package/src/rtc/__tests__/mocks/webrtc.mocks.ts +16 -15
  38. package/src/rtc/types.ts +1 -0
  39. package/src/stats/rtc/StatsTracer.ts +25 -4
  40. package/src/stats/rtc/__tests__/StatsTracer.test.ts +155 -0
package/dist/index.cjs.js CHANGED
@@ -1206,6 +1206,14 @@ var SdkType;
1206
1206
  * @generated from protobuf enum value: SDK_TYPE_PLAIN_JAVASCRIPT = 9;
1207
1207
  */
1208
1208
  SdkType[SdkType["PLAIN_JAVASCRIPT"] = 9] = "PLAIN_JAVASCRIPT";
1209
+ /**
1210
+ * @generated from protobuf enum value: SDK_TYPE_PYTHON = 10;
1211
+ */
1212
+ SdkType[SdkType["PYTHON"] = 10] = "PYTHON";
1213
+ /**
1214
+ * @generated from protobuf enum value: SDK_TYPE_VISION_AGENTS = 11;
1215
+ */
1216
+ SdkType[SdkType["VISION_AGENTS"] = 11] = "VISION_AGENTS";
1209
1217
  })(SdkType || (SdkType = {}));
1210
1218
  /**
1211
1219
  * @generated from protobuf enum stream.video.sfu.models.TrackUnpublishReason
@@ -1417,6 +1425,12 @@ var ClientCapability;
1417
1425
  * @generated from protobuf enum value: CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE = 1;
1418
1426
  */
1419
1427
  ClientCapability[ClientCapability["SUBSCRIBER_VIDEO_PAUSE"] = 1] = "SUBSCRIBER_VIDEO_PAUSE";
1428
+ /**
1429
+ * Instructs SFU that stats will be sent to the coordinator
1430
+ *
1431
+ * @generated from protobuf enum value: CLIENT_CAPABILITY_COORDINATOR_STATS = 2;
1432
+ */
1433
+ ClientCapability[ClientCapability["COORDINATOR_STATS"] = 2] = "COORDINATOR_STATS";
1420
1434
  })(ClientCapability || (ClientCapability = {}));
1421
1435
  /**
1422
1436
  * DegradationPreference represents the RTCDegradationPreference from WebRTC.
@@ -1882,6 +1896,12 @@ class ClientDetails$Type extends runtime.MessageType {
1882
1896
  { no: 2, name: 'os', kind: 'message', T: () => OS },
1883
1897
  { no: 3, name: 'browser', kind: 'message', T: () => Browser },
1884
1898
  { no: 4, name: 'device', kind: 'message', T: () => Device },
1899
+ {
1900
+ no: 5,
1901
+ name: 'webrtc_version',
1902
+ kind: 'scalar',
1903
+ T: 9 /*ScalarType.STRING*/,
1904
+ },
1885
1905
  ]);
1886
1906
  }
1887
1907
  }
@@ -2154,6 +2174,171 @@ class PerformanceStats$Type extends runtime.MessageType {
2154
2174
  * @generated MessageType for protobuf message stream.video.sfu.models.PerformanceStats
2155
2175
  */
2156
2176
  const PerformanceStats = new PerformanceStats$Type();
2177
+ // @generated message type with reflection information, may provide speed optimized methods
2178
+ class RtpBase$Type extends runtime.MessageType {
2179
+ constructor() {
2180
+ super('stream.video.sfu.models.RtpBase', [
2181
+ { no: 1, name: 'ssrc', kind: 'scalar', T: 13 /*ScalarType.UINT32*/ },
2182
+ { no: 2, name: 'kind', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2183
+ {
2184
+ no: 3,
2185
+ name: 'timestamp_ms',
2186
+ kind: 'scalar',
2187
+ T: 1 /*ScalarType.DOUBLE*/,
2188
+ },
2189
+ ]);
2190
+ }
2191
+ }
2192
+ /**
2193
+ * @generated MessageType for protobuf message stream.video.sfu.models.RtpBase
2194
+ */
2195
+ const RtpBase = new RtpBase$Type();
2196
+ // @generated message type with reflection information, may provide speed optimized methods
2197
+ class InboundRtp$Type extends runtime.MessageType {
2198
+ constructor() {
2199
+ super('stream.video.sfu.models.InboundRtp', [
2200
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2201
+ {
2202
+ no: 2,
2203
+ name: 'jitter_seconds',
2204
+ kind: 'scalar',
2205
+ T: 1 /*ScalarType.DOUBLE*/,
2206
+ },
2207
+ {
2208
+ no: 3,
2209
+ name: 'packets_received',
2210
+ kind: 'scalar',
2211
+ T: 4 /*ScalarType.UINT64*/,
2212
+ },
2213
+ {
2214
+ no: 4,
2215
+ name: 'packets_lost',
2216
+ kind: 'scalar',
2217
+ T: 4 /*ScalarType.UINT64*/,
2218
+ },
2219
+ {
2220
+ no: 5,
2221
+ name: 'packet_loss_percent',
2222
+ kind: 'scalar',
2223
+ T: 1 /*ScalarType.DOUBLE*/,
2224
+ },
2225
+ {
2226
+ no: 10,
2227
+ name: 'concealment_events',
2228
+ kind: 'scalar',
2229
+ T: 13 /*ScalarType.UINT32*/,
2230
+ },
2231
+ {
2232
+ no: 11,
2233
+ name: 'concealment_percent',
2234
+ kind: 'scalar',
2235
+ T: 1 /*ScalarType.DOUBLE*/,
2236
+ },
2237
+ { no: 20, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
2238
+ {
2239
+ no: 21,
2240
+ name: 'freeze_duration_seconds',
2241
+ kind: 'scalar',
2242
+ T: 1 /*ScalarType.DOUBLE*/,
2243
+ },
2244
+ {
2245
+ no: 22,
2246
+ name: 'avg_decode_time_seconds',
2247
+ kind: 'scalar',
2248
+ T: 1 /*ScalarType.DOUBLE*/,
2249
+ },
2250
+ {
2251
+ no: 23,
2252
+ name: 'min_dimension_px',
2253
+ kind: 'scalar',
2254
+ T: 13 /*ScalarType.UINT32*/,
2255
+ },
2256
+ ]);
2257
+ }
2258
+ }
2259
+ /**
2260
+ * @generated MessageType for protobuf message stream.video.sfu.models.InboundRtp
2261
+ */
2262
+ const InboundRtp = new InboundRtp$Type();
2263
+ // @generated message type with reflection information, may provide speed optimized methods
2264
+ class OutboundRtp$Type extends runtime.MessageType {
2265
+ constructor() {
2266
+ super('stream.video.sfu.models.OutboundRtp', [
2267
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2268
+ { no: 10, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
2269
+ {
2270
+ no: 11,
2271
+ name: 'avg_encode_time_seconds',
2272
+ kind: 'scalar',
2273
+ T: 1 /*ScalarType.DOUBLE*/,
2274
+ },
2275
+ {
2276
+ no: 12,
2277
+ name: 'bitrate_bps',
2278
+ kind: 'scalar',
2279
+ T: 1 /*ScalarType.DOUBLE*/,
2280
+ },
2281
+ {
2282
+ no: 13,
2283
+ name: 'min_dimension_px',
2284
+ kind: 'scalar',
2285
+ T: 13 /*ScalarType.UINT32*/,
2286
+ },
2287
+ ]);
2288
+ }
2289
+ }
2290
+ /**
2291
+ * @generated MessageType for protobuf message stream.video.sfu.models.OutboundRtp
2292
+ */
2293
+ const OutboundRtp = new OutboundRtp$Type();
2294
+ // @generated message type with reflection information, may provide speed optimized methods
2295
+ class RemoteInboundRtp$Type extends runtime.MessageType {
2296
+ constructor() {
2297
+ super('stream.video.sfu.models.RemoteInboundRtp', [
2298
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2299
+ {
2300
+ no: 2,
2301
+ name: 'jitter_seconds',
2302
+ kind: 'scalar',
2303
+ T: 1 /*ScalarType.DOUBLE*/,
2304
+ },
2305
+ {
2306
+ no: 3,
2307
+ name: 'round_trip_time_s',
2308
+ kind: 'scalar',
2309
+ T: 1 /*ScalarType.DOUBLE*/,
2310
+ },
2311
+ ]);
2312
+ }
2313
+ }
2314
+ /**
2315
+ * @generated MessageType for protobuf message stream.video.sfu.models.RemoteInboundRtp
2316
+ */
2317
+ const RemoteInboundRtp = new RemoteInboundRtp$Type();
2318
+ // @generated message type with reflection information, may provide speed optimized methods
2319
+ class RemoteOutboundRtp$Type extends runtime.MessageType {
2320
+ constructor() {
2321
+ super('stream.video.sfu.models.RemoteOutboundRtp', [
2322
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2323
+ {
2324
+ no: 2,
2325
+ name: 'jitter_seconds',
2326
+ kind: 'scalar',
2327
+ T: 1 /*ScalarType.DOUBLE*/,
2328
+ },
2329
+ {
2330
+ no: 3,
2331
+ name: 'round_trip_time_s',
2332
+ kind: 'scalar',
2333
+ T: 1 /*ScalarType.DOUBLE*/,
2334
+ },
2335
+ ]);
2336
+ }
2337
+ }
2338
+ /**
2339
+ * @generated MessageType for protobuf message stream.video.sfu.models.RemoteOutboundRtp
2340
+ */
2341
+ const RemoteOutboundRtp = new RemoteOutboundRtp$Type();
2157
2342
 
2158
2343
  var models = /*#__PURE__*/Object.freeze({
2159
2344
  __proto__: null,
@@ -2178,8 +2363,10 @@ var models = /*#__PURE__*/Object.freeze({
2178
2363
  get ErrorCode () { return ErrorCode; },
2179
2364
  get GoAwayReason () { return GoAwayReason; },
2180
2365
  ICETrickle: ICETrickle$1,
2366
+ InboundRtp: InboundRtp,
2181
2367
  InputDevices: InputDevices,
2182
2368
  OS: OS,
2369
+ OutboundRtp: OutboundRtp,
2183
2370
  Participant: Participant,
2184
2371
  ParticipantCount: ParticipantCount,
2185
2372
  get ParticipantSource () { return ParticipantSource; },
@@ -2188,6 +2375,9 @@ var models = /*#__PURE__*/Object.freeze({
2188
2375
  Pin: Pin,
2189
2376
  PublishOption: PublishOption,
2190
2377
  RTMPIngress: RTMPIngress,
2378
+ RemoteInboundRtp: RemoteInboundRtp,
2379
+ RemoteOutboundRtp: RemoteOutboundRtp,
2380
+ RtpBase: RtpBase,
2191
2381
  Sdk: Sdk,
2192
2382
  get SdkType () { return SdkType; },
2193
2383
  StreamQuality: StreamQuality,
@@ -2305,6 +2495,62 @@ class Telemetry$Type extends runtime.MessageType {
2305
2495
  */
2306
2496
  const Telemetry = new Telemetry$Type();
2307
2497
  // @generated message type with reflection information, may provide speed optimized methods
2498
+ class SendMetricsRequest$Type extends runtime.MessageType {
2499
+ constructor() {
2500
+ super('stream.video.sfu.signal.SendMetricsRequest', [
2501
+ { no: 1, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2502
+ {
2503
+ no: 2,
2504
+ name: 'unified_session_id',
2505
+ kind: 'scalar',
2506
+ T: 9 /*ScalarType.STRING*/,
2507
+ },
2508
+ {
2509
+ no: 3,
2510
+ name: 'inbounds',
2511
+ kind: 'message',
2512
+ repeat: 2 /*RepeatType.UNPACKED*/,
2513
+ T: () => InboundRtp,
2514
+ },
2515
+ {
2516
+ no: 4,
2517
+ name: 'outbounds',
2518
+ kind: 'message',
2519
+ repeat: 2 /*RepeatType.UNPACKED*/,
2520
+ T: () => OutboundRtp,
2521
+ },
2522
+ {
2523
+ no: 5,
2524
+ name: 'remote_inbounds',
2525
+ kind: 'message',
2526
+ repeat: 2 /*RepeatType.UNPACKED*/,
2527
+ T: () => RemoteInboundRtp,
2528
+ },
2529
+ {
2530
+ no: 6,
2531
+ name: 'remote_outbounds',
2532
+ kind: 'message',
2533
+ repeat: 2 /*RepeatType.UNPACKED*/,
2534
+ T: () => RemoteOutboundRtp,
2535
+ },
2536
+ ]);
2537
+ }
2538
+ }
2539
+ /**
2540
+ * @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsRequest
2541
+ */
2542
+ const SendMetricsRequest = new SendMetricsRequest$Type();
2543
+ // @generated message type with reflection information, may provide speed optimized methods
2544
+ class SendMetricsResponse$Type extends runtime.MessageType {
2545
+ constructor() {
2546
+ super('stream.video.sfu.signal.SendMetricsResponse', []);
2547
+ }
2548
+ }
2549
+ /**
2550
+ * @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsResponse
2551
+ */
2552
+ const SendMetricsResponse = new SendMetricsResponse$Type();
2553
+ // @generated message type with reflection information, may provide speed optimized methods
2308
2554
  class SendStatsRequest$Type extends runtime.MessageType {
2309
2555
  constructor() {
2310
2556
  super('stream.video.sfu.signal.SendStatsRequest', [
@@ -2578,6 +2824,12 @@ class SendAnswerRequest$Type extends runtime.MessageType {
2578
2824
  },
2579
2825
  { no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2580
2826
  { no: 3, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2827
+ {
2828
+ no: 4,
2829
+ name: 'negotiation_id',
2830
+ kind: 'scalar',
2831
+ T: 13 /*ScalarType.UINT32*/,
2832
+ },
2581
2833
  ]);
2582
2834
  }
2583
2835
  }
@@ -2685,6 +2937,12 @@ const SignalServer = new runtimeRpc.ServiceType('stream.video.sfu.signal.SignalS
2685
2937
  I: SendStatsRequest,
2686
2938
  O: SendStatsResponse,
2687
2939
  },
2940
+ {
2941
+ name: 'SendMetrics',
2942
+ options: {},
2943
+ I: SendMetricsRequest,
2944
+ O: SendMetricsResponse,
2945
+ },
2688
2946
  {
2689
2947
  name: 'StartNoiseCancellation',
2690
2948
  options: {},
@@ -3365,6 +3623,12 @@ class SubscriberOffer$Type extends runtime.MessageType {
3365
3623
  super('stream.video.sfu.event.SubscriberOffer', [
3366
3624
  { no: 1, name: 'ice_restart', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
3367
3625
  { no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
3626
+ {
3627
+ no: 3,
3628
+ name: 'negotiation_id',
3629
+ kind: 'scalar',
3630
+ T: 13 /*ScalarType.UINT32*/,
3631
+ },
3368
3632
  ]);
3369
3633
  }
3370
3634
  }
@@ -3823,18 +4087,25 @@ class SignalServerClient {
3823
4087
  const method = this.methods[6], opt = this._transport.mergeOptions(options);
3824
4088
  return runtimeRpc.stackIntercept('unary', this._transport, method, opt, input);
3825
4089
  }
4090
+ /**
4091
+ * @generated from protobuf rpc: SendMetrics(stream.video.sfu.signal.SendMetricsRequest) returns (stream.video.sfu.signal.SendMetricsResponse);
4092
+ */
4093
+ sendMetrics(input, options) {
4094
+ const method = this.methods[7], opt = this._transport.mergeOptions(options);
4095
+ return runtimeRpc.stackIntercept('unary', this._transport, method, opt, input);
4096
+ }
3826
4097
  /**
3827
4098
  * @generated from protobuf rpc: StartNoiseCancellation(stream.video.sfu.signal.StartNoiseCancellationRequest) returns (stream.video.sfu.signal.StartNoiseCancellationResponse);
3828
4099
  */
3829
4100
  startNoiseCancellation(input, options) {
3830
- const method = this.methods[7], opt = this._transport.mergeOptions(options);
4101
+ const method = this.methods[8], opt = this._transport.mergeOptions(options);
3831
4102
  return runtimeRpc.stackIntercept('unary', this._transport, method, opt, input);
3832
4103
  }
3833
4104
  /**
3834
4105
  * @generated from protobuf rpc: StopNoiseCancellation(stream.video.sfu.signal.StopNoiseCancellationRequest) returns (stream.video.sfu.signal.StopNoiseCancellationResponse);
3835
4106
  */
3836
4107
  stopNoiseCancellation(input, options) {
3837
- const method = this.methods[8], opt = this._transport.mergeOptions(options);
4108
+ const method = this.methods[9], opt = this._transport.mergeOptions(options);
3838
4109
  return runtimeRpc.stackIntercept('unary', this._transport, method, opt, input);
3839
4110
  }
3840
4111
  }
@@ -5014,6 +5285,16 @@ const hasScreenShareAudio = (p) => p.publishedTracks.includes(TrackType.SCREEN_S
5014
5285
  * @param p the participant.
5015
5286
  */
5016
5287
  const isPinned = (p) => !!p.pin && (p.pin.isLocalPin || p.pin.pinnedAt > 0);
5288
+ /**
5289
+ * Check if a participant has a track that is currently interrupted: the
5290
+ * participant intends to publish it (it is in `publishedTracks`) but no
5291
+ * media is flowing right now (it is in `interruptedTracks`).
5292
+ *
5293
+ * @param p the participant to check.
5294
+ * @param trackType the track type to check.
5295
+ */
5296
+ const hasInterruptedTrack = (p, trackType) => !!p.interruptedTracks?.includes(trackType) &&
5297
+ p.publishedTracks.includes(trackType);
5017
5298
  /**
5018
5299
  * Check if a participant has a paused track of the specified type.
5019
5300
  *
@@ -6379,7 +6660,7 @@ const getSdkVersion = (sdk) => {
6379
6660
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6380
6661
  };
6381
6662
 
6382
- const version = "1.51.0";
6663
+ const version = "1.52.0";
6383
6664
  const [major, minor, patch] = version.split('.');
6384
6665
  let sdkInfo = {
6385
6666
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -6477,6 +6758,7 @@ const getClientDetails = async () => {
6477
6758
  sdk: sdkInfo,
6478
6759
  os: osInfo,
6479
6760
  device: deviceInfo,
6761
+ webrtcVersion: webRtcInfo?.version || '',
6480
6762
  };
6481
6763
  }
6482
6764
  // @ts-expect-error - userAgentData is not yet in the TS types
@@ -6504,11 +6786,12 @@ const getClientDetails = async () => {
6504
6786
  // Eliminates the generic "Chromium" name and "Not)A_Brand" name from the list.
6505
6787
  // https://wicg.github.io/ua-client-hints/#create-arbitrary-brands-section
6506
6788
  const uaBrowser = userAgentData?.fullVersionList?.find((v) => !v.brand.includes('Chromium') && !v.brand.match(/[()\-./:;=?_]/g));
6789
+ const browserVersion = uaBrowser?.version || browser.version || '';
6507
6790
  return {
6508
6791
  sdk: sdkInfo,
6509
6792
  browser: {
6510
6793
  name: uaBrowser?.brand || browser.name || navigator.userAgent,
6511
- version: uaBrowser?.version || browser.version || '',
6794
+ version: browserVersion,
6512
6795
  },
6513
6796
  os: {
6514
6797
  name: userAgentData?.platform || os.name || '',
@@ -6521,6 +6804,7 @@ const getClientDetails = async () => {
6521
6804
  .join(' '),
6522
6805
  version: '',
6523
6806
  },
6807
+ webrtcVersion: browserVersion,
6524
6808
  };
6525
6809
  };
6526
6810
 
@@ -7184,7 +7468,7 @@ class StatsTracer {
7184
7468
  /**
7185
7469
  * Creates a new StatsTracer instance.
7186
7470
  */
7187
- constructor(pc, peerType, trackIdToTrackType) {
7471
+ constructor(pc, peerType, trackIdToTrackType, statsTimestampDriftThresholdMs = 0) {
7188
7472
  this.previousStats = {};
7189
7473
  this.frameTimeHistory = [];
7190
7474
  this.fpsHistory = [];
@@ -7198,7 +7482,7 @@ class StatsTracer {
7198
7482
  */
7199
7483
  this.get = async () => {
7200
7484
  const stats = await this.pc.getStats();
7201
- const currentStats = toObject(stats);
7485
+ const currentStats = toObjectWithCorrectedTimestamp(stats, Date.now(), this.driftThresholdMs);
7202
7486
  const performanceStats = this.withOverrides(this.peerType === PeerType.SUBSCRIBER
7203
7487
  ? this.getDecodeStats(currentStats)
7204
7488
  : this.getEncodeStats(currentStats));
@@ -7317,17 +7601,28 @@ class StatsTracer {
7317
7601
  this.pc = pc;
7318
7602
  this.peerType = peerType;
7319
7603
  this.trackIdToTrackType = trackIdToTrackType;
7604
+ this.driftThresholdMs = statsTimestampDriftThresholdMs;
7320
7605
  }
7321
7606
  }
7322
7607
  /**
7323
- * Convert the stat report to an object.
7608
+ * Convert the stat report to an object, correcting clock drift along the way.
7609
+ * Entries whose `timestamp` differs from `wallNow` by more than `thresholdMs`
7610
+ * are replaced with a clone whose `timestamp` is set to `wallNow`. The platform
7611
+ * clock backing `DOMHighResTimeStamp` can desynchronise from `Date.now()` after
7612
+ * system sleep or clock-jump events (notably on Electron/Chromium), which
7613
+ * corrupts the delta-compressed stats payload. A non-positive `thresholdMs`
7614
+ * disables correction.
7324
7615
  *
7325
7616
  * @param report the stat report to convert.
7617
+ * @param wallNow current wall-clock time used as the drift reference.
7618
+ * @param thresholdMs maximum tolerated drift in milliseconds.
7326
7619
  */
7327
- const toObject = (report) => {
7620
+ const toObjectWithCorrectedTimestamp = (report, wallNow, thresholdMs) => {
7328
7621
  const obj = {};
7622
+ const correct = thresholdMs > 0;
7329
7623
  report.forEach((v, k) => {
7330
- obj[k] = v;
7624
+ const drift = Math.abs(v.timestamp - wallNow);
7625
+ obj[k] = correct && drift > thresholdMs ? { ...v, timestamp: wallNow } : v;
7331
7626
  });
7332
7627
  return obj;
7333
7628
  };
@@ -7459,7 +7754,7 @@ class BasePeerConnection {
7459
7754
  /**
7460
7755
  * Constructs a new `BasePeerConnection` instance.
7461
7756
  */
7462
- constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, }) {
7757
+ constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, statsTimestampDriftThresholdMs = 0, }) {
7463
7758
  this.iceHasEverConnected = false;
7464
7759
  this.isIceRestarting = false;
7465
7760
  this.isDisposed = false;
@@ -7753,7 +8048,7 @@ class BasePeerConnection {
7753
8048
  this.onIceConnected = onIceConnected;
7754
8049
  this.logger = videoLoggerSystem.getLogger(peerType === PeerType.SUBSCRIBER ? 'Subscriber' : 'Publisher', { tags: [tag] });
7755
8050
  this.pc = this.createPeerConnection(connectionConfig);
7756
- this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType);
8051
+ this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType, statsTimestampDriftThresholdMs);
7757
8052
  if (enableTracing) {
7758
8053
  this.tracer = new Tracer(`${tag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}`);
7759
8054
  this.tracer.trace('create', {
@@ -8839,6 +9134,7 @@ class Subscriber extends BasePeerConnection {
8839
9134
  await this.sfuClient.sendAnswer({
8840
9135
  peerType: PeerType.SUBSCRIBER,
8841
9136
  sdp: answer.sdp || '',
9137
+ negotiationId: subscriberOffer.negotiationId,
8842
9138
  });
8843
9139
  this.isIceRestarting = false;
8844
9140
  };
@@ -14384,7 +14680,7 @@ class Call {
14384
14680
  */
14385
14681
  this.initPublisherAndSubscriber = async (opts) => {
14386
14682
  const { sfuClient, connectionConfig, clientDetails, statsOptions, publishOptions, closePreviousInstances, unifiedSessionId, } = opts;
14387
- const { enable_rtc_stats: enableTracing } = statsOptions;
14683
+ const { enable_rtc_stats: enableTracing, reporting_interval_ms: reportingIntervalMs, } = statsOptions;
14388
14684
  if (closePreviousInstances && this.subscriber) {
14389
14685
  await this.subscriber.dispose();
14390
14686
  }
@@ -14395,6 +14691,7 @@ class Call {
14395
14691
  connectionConfig,
14396
14692
  tag: sfuClient.tag,
14397
14693
  enableTracing,
14694
+ statsTimestampDriftThresholdMs: reportingIntervalMs / 2,
14398
14695
  clientPublishOptions: this.clientPublishOptions,
14399
14696
  onReconnectionNeeded: (kind, reason, peerType) => {
14400
14697
  this.reconnect(kind, reason).catch((err) => {
@@ -17036,7 +17333,7 @@ class StreamClient {
17036
17333
  this.getUserAgent = () => {
17037
17334
  if (!this.cachedUserAgent) {
17038
17335
  const { clientAppIdentifier = {} } = this.options;
17039
- const { sdkName = 'js', sdkVersion = "1.51.0", ...extras } = clientAppIdentifier;
17336
+ const { sdkName = 'js', sdkVersion = "1.52.0", ...extras } = clientAppIdentifier;
17040
17337
  this.cachedUserAgent = [
17041
17338
  `stream-video-${sdkName}-v${sdkVersion}`,
17042
17339
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
@@ -17762,6 +18059,7 @@ exports.getVideoDevices = getVideoDevices;
17762
18059
  exports.getVideoStream = getVideoStream;
17763
18060
  exports.getWebRTCInfo = getWebRTCInfo;
17764
18061
  exports.hasAudio = hasAudio$1;
18062
+ exports.hasInterruptedTrack = hasInterruptedTrack;
17765
18063
  exports.hasPausedTrack = hasPausedTrack;
17766
18064
  exports.hasScreenShare = hasScreenShare;
17767
18065
  exports.hasScreenShareAudio = hasScreenShareAudio;