@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.cjs.js CHANGED
@@ -527,6 +527,7 @@ class ErrorFromResponse extends Error {
527
527
  }
528
528
  }
529
529
 
530
+ /* eslint-disable */
530
531
  // @generated by protobuf-ts 2.10.0 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
531
532
  // @generated from protobuf file "google/protobuf/struct.proto" (package "google.protobuf", syntax proto3)
532
533
  // tslint:disable
@@ -792,6 +793,7 @@ class ListValue$Type extends runtime.MessageType {
792
793
  */
793
794
  const ListValue = new ListValue$Type();
794
795
 
796
+ /* eslint-disable */
795
797
  // @generated by protobuf-ts 2.10.0 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
796
798
  // @generated from protobuf file "google/protobuf/timestamp.proto" (package "google.protobuf", syntax proto3)
797
799
  // tslint:disable
@@ -1206,6 +1208,14 @@ var SdkType;
1206
1208
  * @generated from protobuf enum value: SDK_TYPE_PLAIN_JAVASCRIPT = 9;
1207
1209
  */
1208
1210
  SdkType[SdkType["PLAIN_JAVASCRIPT"] = 9] = "PLAIN_JAVASCRIPT";
1211
+ /**
1212
+ * @generated from protobuf enum value: SDK_TYPE_PYTHON = 10;
1213
+ */
1214
+ SdkType[SdkType["PYTHON"] = 10] = "PYTHON";
1215
+ /**
1216
+ * @generated from protobuf enum value: SDK_TYPE_VISION_AGENTS = 11;
1217
+ */
1218
+ SdkType[SdkType["VISION_AGENTS"] = 11] = "VISION_AGENTS";
1209
1219
  })(SdkType || (SdkType = {}));
1210
1220
  /**
1211
1221
  * @generated from protobuf enum stream.video.sfu.models.TrackUnpublishReason
@@ -1417,6 +1427,12 @@ var ClientCapability;
1417
1427
  * @generated from protobuf enum value: CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE = 1;
1418
1428
  */
1419
1429
  ClientCapability[ClientCapability["SUBSCRIBER_VIDEO_PAUSE"] = 1] = "SUBSCRIBER_VIDEO_PAUSE";
1430
+ /**
1431
+ * Instructs SFU that stats will be sent to the coordinator
1432
+ *
1433
+ * @generated from protobuf enum value: CLIENT_CAPABILITY_COORDINATOR_STATS = 2;
1434
+ */
1435
+ ClientCapability[ClientCapability["COORDINATOR_STATS"] = 2] = "COORDINATOR_STATS";
1420
1436
  })(ClientCapability || (ClientCapability = {}));
1421
1437
  /**
1422
1438
  * DegradationPreference represents the RTCDegradationPreference from WebRTC.
@@ -1844,6 +1860,12 @@ class TrackInfo$Type extends runtime.MessageType {
1844
1860
  kind: 'scalar',
1845
1861
  T: 5 /*ScalarType.INT32*/,
1846
1862
  },
1863
+ {
1864
+ no: 13,
1865
+ name: 'self_sub_audio_video',
1866
+ kind: 'scalar',
1867
+ T: 8 /*ScalarType.BOOL*/,
1868
+ },
1847
1869
  ]);
1848
1870
  }
1849
1871
  }
@@ -1882,6 +1904,12 @@ class ClientDetails$Type extends runtime.MessageType {
1882
1904
  { no: 2, name: 'os', kind: 'message', T: () => OS },
1883
1905
  { no: 3, name: 'browser', kind: 'message', T: () => Browser },
1884
1906
  { no: 4, name: 'device', kind: 'message', T: () => Device },
1907
+ {
1908
+ no: 5,
1909
+ name: 'webrtc_version',
1910
+ kind: 'scalar',
1911
+ T: 9 /*ScalarType.STRING*/,
1912
+ },
1885
1913
  ]);
1886
1914
  }
1887
1915
  }
@@ -2154,6 +2182,171 @@ class PerformanceStats$Type extends runtime.MessageType {
2154
2182
  * @generated MessageType for protobuf message stream.video.sfu.models.PerformanceStats
2155
2183
  */
2156
2184
  const PerformanceStats = new PerformanceStats$Type();
2185
+ // @generated message type with reflection information, may provide speed optimized methods
2186
+ class RtpBase$Type extends runtime.MessageType {
2187
+ constructor() {
2188
+ super('stream.video.sfu.models.RtpBase', [
2189
+ { no: 1, name: 'ssrc', kind: 'scalar', T: 13 /*ScalarType.UINT32*/ },
2190
+ { no: 2, name: 'kind', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2191
+ {
2192
+ no: 3,
2193
+ name: 'timestamp_ms',
2194
+ kind: 'scalar',
2195
+ T: 1 /*ScalarType.DOUBLE*/,
2196
+ },
2197
+ ]);
2198
+ }
2199
+ }
2200
+ /**
2201
+ * @generated MessageType for protobuf message stream.video.sfu.models.RtpBase
2202
+ */
2203
+ const RtpBase = new RtpBase$Type();
2204
+ // @generated message type with reflection information, may provide speed optimized methods
2205
+ class InboundRtp$Type extends runtime.MessageType {
2206
+ constructor() {
2207
+ super('stream.video.sfu.models.InboundRtp', [
2208
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2209
+ {
2210
+ no: 2,
2211
+ name: 'jitter_seconds',
2212
+ kind: 'scalar',
2213
+ T: 1 /*ScalarType.DOUBLE*/,
2214
+ },
2215
+ {
2216
+ no: 3,
2217
+ name: 'packets_received',
2218
+ kind: 'scalar',
2219
+ T: 4 /*ScalarType.UINT64*/,
2220
+ },
2221
+ {
2222
+ no: 4,
2223
+ name: 'packets_lost',
2224
+ kind: 'scalar',
2225
+ T: 4 /*ScalarType.UINT64*/,
2226
+ },
2227
+ {
2228
+ no: 5,
2229
+ name: 'packet_loss_percent',
2230
+ kind: 'scalar',
2231
+ T: 1 /*ScalarType.DOUBLE*/,
2232
+ },
2233
+ {
2234
+ no: 10,
2235
+ name: 'concealment_events',
2236
+ kind: 'scalar',
2237
+ T: 13 /*ScalarType.UINT32*/,
2238
+ },
2239
+ {
2240
+ no: 11,
2241
+ name: 'concealment_percent',
2242
+ kind: 'scalar',
2243
+ T: 1 /*ScalarType.DOUBLE*/,
2244
+ },
2245
+ { no: 20, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
2246
+ {
2247
+ no: 21,
2248
+ name: 'freeze_duration_seconds',
2249
+ kind: 'scalar',
2250
+ T: 1 /*ScalarType.DOUBLE*/,
2251
+ },
2252
+ {
2253
+ no: 22,
2254
+ name: 'avg_decode_time_seconds',
2255
+ kind: 'scalar',
2256
+ T: 1 /*ScalarType.DOUBLE*/,
2257
+ },
2258
+ {
2259
+ no: 23,
2260
+ name: 'min_dimension_px',
2261
+ kind: 'scalar',
2262
+ T: 13 /*ScalarType.UINT32*/,
2263
+ },
2264
+ ]);
2265
+ }
2266
+ }
2267
+ /**
2268
+ * @generated MessageType for protobuf message stream.video.sfu.models.InboundRtp
2269
+ */
2270
+ const InboundRtp = new InboundRtp$Type();
2271
+ // @generated message type with reflection information, may provide speed optimized methods
2272
+ class OutboundRtp$Type extends runtime.MessageType {
2273
+ constructor() {
2274
+ super('stream.video.sfu.models.OutboundRtp', [
2275
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2276
+ { no: 10, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
2277
+ {
2278
+ no: 11,
2279
+ name: 'avg_encode_time_seconds',
2280
+ kind: 'scalar',
2281
+ T: 1 /*ScalarType.DOUBLE*/,
2282
+ },
2283
+ {
2284
+ no: 12,
2285
+ name: 'bitrate_bps',
2286
+ kind: 'scalar',
2287
+ T: 1 /*ScalarType.DOUBLE*/,
2288
+ },
2289
+ {
2290
+ no: 13,
2291
+ name: 'min_dimension_px',
2292
+ kind: 'scalar',
2293
+ T: 13 /*ScalarType.UINT32*/,
2294
+ },
2295
+ ]);
2296
+ }
2297
+ }
2298
+ /**
2299
+ * @generated MessageType for protobuf message stream.video.sfu.models.OutboundRtp
2300
+ */
2301
+ const OutboundRtp = new OutboundRtp$Type();
2302
+ // @generated message type with reflection information, may provide speed optimized methods
2303
+ class RemoteInboundRtp$Type extends runtime.MessageType {
2304
+ constructor() {
2305
+ super('stream.video.sfu.models.RemoteInboundRtp', [
2306
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2307
+ {
2308
+ no: 2,
2309
+ name: 'jitter_seconds',
2310
+ kind: 'scalar',
2311
+ T: 1 /*ScalarType.DOUBLE*/,
2312
+ },
2313
+ {
2314
+ no: 3,
2315
+ name: 'round_trip_time_s',
2316
+ kind: 'scalar',
2317
+ T: 1 /*ScalarType.DOUBLE*/,
2318
+ },
2319
+ ]);
2320
+ }
2321
+ }
2322
+ /**
2323
+ * @generated MessageType for protobuf message stream.video.sfu.models.RemoteInboundRtp
2324
+ */
2325
+ const RemoteInboundRtp = new RemoteInboundRtp$Type();
2326
+ // @generated message type with reflection information, may provide speed optimized methods
2327
+ class RemoteOutboundRtp$Type extends runtime.MessageType {
2328
+ constructor() {
2329
+ super('stream.video.sfu.models.RemoteOutboundRtp', [
2330
+ { no: 1, name: 'base', kind: 'message', T: () => RtpBase },
2331
+ {
2332
+ no: 2,
2333
+ name: 'jitter_seconds',
2334
+ kind: 'scalar',
2335
+ T: 1 /*ScalarType.DOUBLE*/,
2336
+ },
2337
+ {
2338
+ no: 3,
2339
+ name: 'round_trip_time_s',
2340
+ kind: 'scalar',
2341
+ T: 1 /*ScalarType.DOUBLE*/,
2342
+ },
2343
+ ]);
2344
+ }
2345
+ }
2346
+ /**
2347
+ * @generated MessageType for protobuf message stream.video.sfu.models.RemoteOutboundRtp
2348
+ */
2349
+ const RemoteOutboundRtp = new RemoteOutboundRtp$Type();
2157
2350
 
2158
2351
  var models = /*#__PURE__*/Object.freeze({
2159
2352
  __proto__: null,
@@ -2178,8 +2371,10 @@ var models = /*#__PURE__*/Object.freeze({
2178
2371
  get ErrorCode () { return ErrorCode; },
2179
2372
  get GoAwayReason () { return GoAwayReason; },
2180
2373
  ICETrickle: ICETrickle$1,
2374
+ InboundRtp: InboundRtp,
2181
2375
  InputDevices: InputDevices,
2182
2376
  OS: OS,
2377
+ OutboundRtp: OutboundRtp,
2183
2378
  Participant: Participant,
2184
2379
  ParticipantCount: ParticipantCount,
2185
2380
  get ParticipantSource () { return ParticipantSource; },
@@ -2188,6 +2383,9 @@ var models = /*#__PURE__*/Object.freeze({
2188
2383
  Pin: Pin,
2189
2384
  PublishOption: PublishOption,
2190
2385
  RTMPIngress: RTMPIngress,
2386
+ RemoteInboundRtp: RemoteInboundRtp,
2387
+ RemoteOutboundRtp: RemoteOutboundRtp,
2388
+ RtpBase: RtpBase,
2191
2389
  Sdk: Sdk,
2192
2390
  get SdkType () { return SdkType; },
2193
2391
  StreamQuality: StreamQuality,
@@ -2305,6 +2503,62 @@ class Telemetry$Type extends runtime.MessageType {
2305
2503
  */
2306
2504
  const Telemetry = new Telemetry$Type();
2307
2505
  // @generated message type with reflection information, may provide speed optimized methods
2506
+ class SendMetricsRequest$Type extends runtime.MessageType {
2507
+ constructor() {
2508
+ super('stream.video.sfu.signal.SendMetricsRequest', [
2509
+ { no: 1, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2510
+ {
2511
+ no: 2,
2512
+ name: 'unified_session_id',
2513
+ kind: 'scalar',
2514
+ T: 9 /*ScalarType.STRING*/,
2515
+ },
2516
+ {
2517
+ no: 3,
2518
+ name: 'inbounds',
2519
+ kind: 'message',
2520
+ repeat: 2 /*RepeatType.UNPACKED*/,
2521
+ T: () => InboundRtp,
2522
+ },
2523
+ {
2524
+ no: 4,
2525
+ name: 'outbounds',
2526
+ kind: 'message',
2527
+ repeat: 2 /*RepeatType.UNPACKED*/,
2528
+ T: () => OutboundRtp,
2529
+ },
2530
+ {
2531
+ no: 5,
2532
+ name: 'remote_inbounds',
2533
+ kind: 'message',
2534
+ repeat: 2 /*RepeatType.UNPACKED*/,
2535
+ T: () => RemoteInboundRtp,
2536
+ },
2537
+ {
2538
+ no: 6,
2539
+ name: 'remote_outbounds',
2540
+ kind: 'message',
2541
+ repeat: 2 /*RepeatType.UNPACKED*/,
2542
+ T: () => RemoteOutboundRtp,
2543
+ },
2544
+ ]);
2545
+ }
2546
+ }
2547
+ /**
2548
+ * @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsRequest
2549
+ */
2550
+ const SendMetricsRequest = new SendMetricsRequest$Type();
2551
+ // @generated message type with reflection information, may provide speed optimized methods
2552
+ class SendMetricsResponse$Type extends runtime.MessageType {
2553
+ constructor() {
2554
+ super('stream.video.sfu.signal.SendMetricsResponse', []);
2555
+ }
2556
+ }
2557
+ /**
2558
+ * @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsResponse
2559
+ */
2560
+ const SendMetricsResponse = new SendMetricsResponse$Type();
2561
+ // @generated message type with reflection information, may provide speed optimized methods
2308
2562
  class SendStatsRequest$Type extends runtime.MessageType {
2309
2563
  constructor() {
2310
2564
  super('stream.video.sfu.signal.SendStatsRequest', [
@@ -2578,6 +2832,12 @@ class SendAnswerRequest$Type extends runtime.MessageType {
2578
2832
  },
2579
2833
  { no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2580
2834
  { no: 3, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
2835
+ {
2836
+ no: 4,
2837
+ name: 'negotiation_id',
2838
+ kind: 'scalar',
2839
+ T: 13 /*ScalarType.UINT32*/,
2840
+ },
2581
2841
  ]);
2582
2842
  }
2583
2843
  }
@@ -2685,6 +2945,12 @@ const SignalServer = new runtimeRpc.ServiceType('stream.video.sfu.signal.SignalS
2685
2945
  I: SendStatsRequest,
2686
2946
  O: SendStatsResponse,
2687
2947
  },
2948
+ {
2949
+ name: 'SendMetrics',
2950
+ options: {},
2951
+ I: SendMetricsRequest,
2952
+ O: SendMetricsResponse,
2953
+ },
2688
2954
  {
2689
2955
  name: 'StartNoiseCancellation',
2690
2956
  options: {},
@@ -3365,6 +3631,12 @@ class SubscriberOffer$Type extends runtime.MessageType {
3365
3631
  super('stream.video.sfu.event.SubscriberOffer', [
3366
3632
  { no: 1, name: 'ice_restart', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
3367
3633
  { no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
3634
+ {
3635
+ no: 3,
3636
+ name: 'negotiation_id',
3637
+ kind: 'scalar',
3638
+ T: 13 /*ScalarType.UINT32*/,
3639
+ },
3368
3640
  ]);
3369
3641
  }
3370
3642
  }
@@ -3823,18 +4095,25 @@ class SignalServerClient {
3823
4095
  const method = this.methods[6], opt = this._transport.mergeOptions(options);
3824
4096
  return runtimeRpc.stackIntercept('unary', this._transport, method, opt, input);
3825
4097
  }
4098
+ /**
4099
+ * @generated from protobuf rpc: SendMetrics(stream.video.sfu.signal.SendMetricsRequest) returns (stream.video.sfu.signal.SendMetricsResponse);
4100
+ */
4101
+ sendMetrics(input, options) {
4102
+ const method = this.methods[7], opt = this._transport.mergeOptions(options);
4103
+ return runtimeRpc.stackIntercept('unary', this._transport, method, opt, input);
4104
+ }
3826
4105
  /**
3827
4106
  * @generated from protobuf rpc: StartNoiseCancellation(stream.video.sfu.signal.StartNoiseCancellationRequest) returns (stream.video.sfu.signal.StartNoiseCancellationResponse);
3828
4107
  */
3829
4108
  startNoiseCancellation(input, options) {
3830
- const method = this.methods[7], opt = this._transport.mergeOptions(options);
4109
+ const method = this.methods[8], opt = this._transport.mergeOptions(options);
3831
4110
  return runtimeRpc.stackIntercept('unary', this._transport, method, opt, input);
3832
4111
  }
3833
4112
  /**
3834
4113
  * @generated from protobuf rpc: StopNoiseCancellation(stream.video.sfu.signal.StopNoiseCancellationRequest) returns (stream.video.sfu.signal.StopNoiseCancellationResponse);
3835
4114
  */
3836
4115
  stopNoiseCancellation(input, options) {
3837
- const method = this.methods[8], opt = this._transport.mergeOptions(options);
4116
+ const method = this.methods[9], opt = this._transport.mergeOptions(options);
3838
4117
  return runtimeRpc.stackIntercept('unary', this._transport, method, opt, input);
3839
4118
  }
3840
4119
  }
@@ -5014,6 +5293,16 @@ const hasScreenShareAudio = (p) => p.publishedTracks.includes(TrackType.SCREEN_S
5014
5293
  * @param p the participant.
5015
5294
  */
5016
5295
  const isPinned = (p) => !!p.pin && (p.pin.isLocalPin || p.pin.pinnedAt > 0);
5296
+ /**
5297
+ * Check if a participant has a track that is currently interrupted: the
5298
+ * participant intends to publish it (it is in `publishedTracks`) but no
5299
+ * media is flowing right now (it is in `interruptedTracks`).
5300
+ *
5301
+ * @param p the participant to check.
5302
+ * @param trackType the track type to check.
5303
+ */
5304
+ const hasInterruptedTrack = (p, trackType) => !!p.interruptedTracks?.includes(trackType) &&
5305
+ p.publishedTracks.includes(trackType);
5017
5306
  /**
5018
5307
  * Check if a participant has a paused track of the specified type.
5019
5308
  *
@@ -6379,7 +6668,7 @@ const getSdkVersion = (sdk) => {
6379
6668
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6380
6669
  };
6381
6670
 
6382
- const version = "1.51.0";
6671
+ const version = "1.52.1-beta.0";
6383
6672
  const [major, minor, patch] = version.split('.');
6384
6673
  let sdkInfo = {
6385
6674
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -6477,6 +6766,7 @@ const getClientDetails = async () => {
6477
6766
  sdk: sdkInfo,
6478
6767
  os: osInfo,
6479
6768
  device: deviceInfo,
6769
+ webrtcVersion: webRtcInfo?.version || '',
6480
6770
  };
6481
6771
  }
6482
6772
  // @ts-expect-error - userAgentData is not yet in the TS types
@@ -6504,11 +6794,12 @@ const getClientDetails = async () => {
6504
6794
  // Eliminates the generic "Chromium" name and "Not)A_Brand" name from the list.
6505
6795
  // https://wicg.github.io/ua-client-hints/#create-arbitrary-brands-section
6506
6796
  const uaBrowser = userAgentData?.fullVersionList?.find((v) => !v.brand.includes('Chromium') && !v.brand.match(/[()\-./:;=?_]/g));
6797
+ const browserVersion = uaBrowser?.version || browser.version || '';
6507
6798
  return {
6508
6799
  sdk: sdkInfo,
6509
6800
  browser: {
6510
6801
  name: uaBrowser?.brand || browser.name || navigator.userAgent,
6511
- version: uaBrowser?.version || browser.version || '',
6802
+ version: browserVersion,
6512
6803
  },
6513
6804
  os: {
6514
6805
  name: userAgentData?.platform || os.name || '',
@@ -6521,6 +6812,7 @@ const getClientDetails = async () => {
6521
6812
  .join(' '),
6522
6813
  version: '',
6523
6814
  },
6815
+ webrtcVersion: webRtcInfo?.version || '',
6524
6816
  };
6525
6817
  };
6526
6818
 
@@ -7184,7 +7476,7 @@ class StatsTracer {
7184
7476
  /**
7185
7477
  * Creates a new StatsTracer instance.
7186
7478
  */
7187
- constructor(pc, peerType, trackIdToTrackType) {
7479
+ constructor(pc, peerType, trackIdToTrackType, statsTimestampDriftThresholdMs = 0) {
7188
7480
  this.previousStats = {};
7189
7481
  this.frameTimeHistory = [];
7190
7482
  this.fpsHistory = [];
@@ -7198,7 +7490,7 @@ class StatsTracer {
7198
7490
  */
7199
7491
  this.get = async () => {
7200
7492
  const stats = await this.pc.getStats();
7201
- const currentStats = toObject(stats);
7493
+ const currentStats = toObjectWithCorrectedTimestamp(stats, Date.now(), this.driftThresholdMs);
7202
7494
  const performanceStats = this.withOverrides(this.peerType === PeerType.SUBSCRIBER
7203
7495
  ? this.getDecodeStats(currentStats)
7204
7496
  : this.getEncodeStats(currentStats));
@@ -7317,17 +7609,28 @@ class StatsTracer {
7317
7609
  this.pc = pc;
7318
7610
  this.peerType = peerType;
7319
7611
  this.trackIdToTrackType = trackIdToTrackType;
7612
+ this.driftThresholdMs = statsTimestampDriftThresholdMs;
7320
7613
  }
7321
7614
  }
7322
7615
  /**
7323
- * Convert the stat report to an object.
7616
+ * Convert the stat report to an object, correcting clock drift along the way.
7617
+ * Entries whose `timestamp` differs from `wallNow` by more than `thresholdMs`
7618
+ * are replaced with a clone whose `timestamp` is set to `wallNow`. The platform
7619
+ * clock backing `DOMHighResTimeStamp` can desynchronise from `Date.now()` after
7620
+ * system sleep or clock-jump events (notably on Electron/Chromium), which
7621
+ * corrupts the delta-compressed stats payload. A non-positive `thresholdMs`
7622
+ * disables correction.
7324
7623
  *
7325
7624
  * @param report the stat report to convert.
7625
+ * @param wallNow current wall-clock time used as the drift reference.
7626
+ * @param thresholdMs maximum tolerated drift in milliseconds.
7326
7627
  */
7327
- const toObject = (report) => {
7628
+ const toObjectWithCorrectedTimestamp = (report, wallNow, thresholdMs) => {
7328
7629
  const obj = {};
7630
+ const correct = thresholdMs > 0;
7329
7631
  report.forEach((v, k) => {
7330
- obj[k] = v;
7632
+ const drift = Math.abs(v.timestamp - wallNow);
7633
+ obj[k] = correct && drift > thresholdMs ? { ...v, timestamp: wallNow } : v;
7331
7634
  });
7332
7635
  return obj;
7333
7636
  };
@@ -7459,7 +7762,7 @@ class BasePeerConnection {
7459
7762
  /**
7460
7763
  * Constructs a new `BasePeerConnection` instance.
7461
7764
  */
7462
- constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, }) {
7765
+ constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, statsTimestampDriftThresholdMs = 0, }) {
7463
7766
  this.iceHasEverConnected = false;
7464
7767
  this.isIceRestarting = false;
7465
7768
  this.isDisposed = false;
@@ -7753,7 +8056,7 @@ class BasePeerConnection {
7753
8056
  this.onIceConnected = onIceConnected;
7754
8057
  this.logger = videoLoggerSystem.getLogger(peerType === PeerType.SUBSCRIBER ? 'Subscriber' : 'Publisher', { tags: [tag] });
7755
8058
  this.pc = this.createPeerConnection(connectionConfig);
7756
- this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType);
8059
+ this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType, statsTimestampDriftThresholdMs);
7757
8060
  if (enableTracing) {
7758
8061
  this.tracer = new Tracer(`${tag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}`);
7759
8062
  this.tracer.trace('create', {
@@ -8133,7 +8436,7 @@ class Publisher extends BasePeerConnection {
8133
8436
  /**
8134
8437
  * Constructs a new `Publisher` instance.
8135
8438
  */
8136
- constructor(baseOptions, publishOptions) {
8439
+ constructor(baseOptions, publishOptions, opts = {}) {
8137
8440
  super(PeerType.PUBLISHER_UNSPECIFIED, baseOptions);
8138
8441
  this.transceiverCache = new TransceiverCache();
8139
8442
  this.clonedTracks = new Set();
@@ -8554,6 +8857,7 @@ class Publisher extends BasePeerConnection {
8554
8857
  muted: !isTrackLive,
8555
8858
  codec: publishOption.codec,
8556
8859
  publishOptionId: publishOption.id,
8860
+ selfSubAudioVideo: this.selfSubEnabled,
8557
8861
  };
8558
8862
  };
8559
8863
  this.cloneTrack = (track) => {
@@ -8634,6 +8938,7 @@ class Publisher extends BasePeerConnection {
8634
8938
  });
8635
8939
  };
8636
8940
  this.publishOptions = publishOptions;
8941
+ this.selfSubEnabled = opts.selfSubEnabled ?? false;
8637
8942
  this.on('iceRestart', (iceRestart) => {
8638
8943
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
8639
8944
  return;
@@ -8715,6 +9020,13 @@ class Subscriber extends BasePeerConnection {
8715
9020
  */
8716
9021
  constructor(opts) {
8717
9022
  super(PeerType.SUBSCRIBER, opts);
9023
+ /**
9024
+ * Remote streams received from the SFU. For a self-sub case
9025
+ * we need to be able to distinguish between the local capture stream.
9026
+ * The map will never contain local streams so we can safely use it to
9027
+ * check if the stream is remote and dispose it when needed.
9028
+ */
9029
+ this.trackedStreams = new WeakSet();
8718
9030
  /**
8719
9031
  * Restarts the ICE connection and renegotiates with the SFU.
8720
9032
  */
@@ -8749,6 +9061,7 @@ class Subscriber extends BasePeerConnection {
8749
9061
  // example: `e3f6aaf8-b03d-4911-be36-83f47d37a76a:TRACK_TYPE_VIDEO`
8750
9062
  const [trackId, rawTrackType] = primaryStream.id.split(':');
8751
9063
  const participantToUpdate = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
9064
+ const isSelfSub = !!participantToUpdate?.isLocalParticipant;
8752
9065
  this.logger.debug(`[onTrack]: Got remote ${rawTrackType} track for userId: ${participantToUpdate?.userId}`, track.id, track);
8753
9066
  const trackType = toTrackType(rawTrackType);
8754
9067
  if (!trackType) {
@@ -8772,6 +9085,9 @@ class Subscriber extends BasePeerConnection {
8772
9085
  this.setRemoteTrackInterrupted(trackId, trackType, true);
8773
9086
  }
8774
9087
  this.trackIdToTrackType.set(track.id, trackType);
9088
+ if (isSelfSub) {
9089
+ this.trackedStreams.add(primaryStream);
9090
+ }
8775
9091
  if (!participantToUpdate) {
8776
9092
  this.logger.warn(`[onTrack]: Received track for unknown participant: ${trackId}`, e);
8777
9093
  this.state.registerOrphanedTrack({
@@ -8787,6 +9103,12 @@ class Subscriber extends BasePeerConnection {
8787
9103
  this.logger.error(`Unknown track type: ${rawTrackType}`);
8788
9104
  return;
8789
9105
  }
9106
+ // Self-sub loopback audio routes to the speaker by default, which
9107
+ // would echo the local user's voice. Default-mute here; consumers
9108
+ // (the loopback recording hook) re-enable explicitly when needed.
9109
+ if (isSelfSub && e.track.kind === 'audio') {
9110
+ e.track.enabled = false;
9111
+ }
8790
9112
  // get the previous stream to dispose it later
8791
9113
  // usually this happens during migration, when the stream is replaced
8792
9114
  // with a new one but the old one is still in the state
@@ -8795,8 +9117,12 @@ class Subscriber extends BasePeerConnection {
8795
9117
  this.state.updateParticipant(participantToUpdate.sessionId, {
8796
9118
  [streamKindProp]: primaryStream,
8797
9119
  });
8798
- // now, dispose the previous stream if it exists
8799
9120
  if (previousStream) {
9121
+ if (isSelfSub && !this.trackedStreams.has(previousStream)) {
9122
+ // this is the local capture stream, we don't want to dispose it
9123
+ this.logger.debug(`[onTrack]: Skipping cleanup of previous ${e.track.kind} stream for userId: ${participantToUpdate.userId} because it is not tracked`);
9124
+ return;
9125
+ }
8800
9126
  this.logger.info(`[onTrack]: Cleaning up previous remote ${track.kind} tracks for userId: ${participantToUpdate.userId}`);
8801
9127
  previousStream.getTracks().forEach((t) => {
8802
9128
  t.stop();
@@ -8839,6 +9165,7 @@ class Subscriber extends BasePeerConnection {
8839
9165
  await this.sfuClient.sendAnswer({
8840
9166
  peerType: PeerType.SUBSCRIBER,
8841
9167
  sdp: answer.sdp || '',
9168
+ negotiationId: subscriberOffer.negotiationId,
8842
9169
  });
8843
9170
  this.isIceRestarting = false;
8844
9171
  };
@@ -13553,6 +13880,7 @@ class Call {
13553
13880
  // maintain the order of publishing tracks to restore them after a reconnection
13554
13881
  // it shouldn't contain duplicates
13555
13882
  this.trackPublishOrder = [];
13883
+ this.selfSubEnabled = false;
13556
13884
  this.hasJoinedOnce = false;
13557
13885
  this.deviceSettingsAppliedOnce = false;
13558
13886
  this.initialized = false;
@@ -13897,6 +14225,30 @@ class Call {
13897
14225
  await Promise.all(stopOnLeavePromises);
13898
14226
  });
13899
14227
  };
14228
+ /**
14229
+ * The largest video publish dimension across the current publish options.
14230
+ *
14231
+ * @internal
14232
+ */
14233
+ this.getMaxVideoPublishDimension = () => {
14234
+ if (!this.currentPublishOptions)
14235
+ return undefined;
14236
+ let maxDimension;
14237
+ let maxArea = 0;
14238
+ for (const opt of this.currentPublishOptions) {
14239
+ if (opt.trackType !== TrackType.VIDEO)
14240
+ continue;
14241
+ const dim = opt.videoDimension;
14242
+ if (!dim || !dim.width || !dim.height)
14243
+ continue;
14244
+ const area = dim.width * dim.height;
14245
+ if (area > maxArea) {
14246
+ maxDimension = dim;
14247
+ maxArea = area;
14248
+ }
14249
+ }
14250
+ return maxDimension;
14251
+ };
13900
14252
  /**
13901
14253
  * Update from the call response from the "call.ring" event
13902
14254
  * @internal
@@ -14043,7 +14395,7 @@ class Call {
14043
14395
  *
14044
14396
  * @returns a promise which resolves once the call join-flow has finished.
14045
14397
  */
14046
- this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, ...data } = {}) => {
14398
+ this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, selfSubEnabled = false, ...data } = {}) => {
14047
14399
  const callingState = this.state.callingState;
14048
14400
  if ([exports.CallingState.JOINED, exports.CallingState.JOINING].includes(callingState)) {
14049
14401
  throw new Error(`Illegal State: call.join() shall be called only once`);
@@ -14051,6 +14403,9 @@ class Call {
14051
14403
  if (data?.ring) {
14052
14404
  this.ringingSubject.next(true);
14053
14405
  }
14406
+ // we need this to be set before the callingx.joinCall() is
14407
+ // called to avoid registering the test call in the CallKit/Telecom
14408
+ this.selfSubEnabled = selfSubEnabled;
14054
14409
  const callingX = globalThis.streamRNVideoSDK?.callingX;
14055
14410
  if (callingX) {
14056
14411
  // for Android/iOS, we need to start the call in the callingx library as soon as possible
@@ -14384,7 +14739,7 @@ class Call {
14384
14739
  */
14385
14740
  this.initPublisherAndSubscriber = async (opts) => {
14386
14741
  const { sfuClient, connectionConfig, clientDetails, statsOptions, publishOptions, closePreviousInstances, unifiedSessionId, } = opts;
14387
- const { enable_rtc_stats: enableTracing } = statsOptions;
14742
+ const { enable_rtc_stats: enableTracing, reporting_interval_ms: reportingIntervalMs, } = statsOptions;
14388
14743
  if (closePreviousInstances && this.subscriber) {
14389
14744
  await this.subscriber.dispose();
14390
14745
  }
@@ -14395,6 +14750,7 @@ class Call {
14395
14750
  connectionConfig,
14396
14751
  tag: sfuClient.tag,
14397
14752
  enableTracing,
14753
+ statsTimestampDriftThresholdMs: reportingIntervalMs / 2,
14398
14754
  clientPublishOptions: this.clientPublishOptions,
14399
14755
  onReconnectionNeeded: (kind, reason, peerType) => {
14400
14756
  this.reconnect(kind, reason).catch((err) => {
@@ -14417,7 +14773,9 @@ class Call {
14417
14773
  if (closePreviousInstances && this.publisher) {
14418
14774
  await this.publisher.dispose();
14419
14775
  }
14420
- this.publisher = new Publisher(basePeerConnectionOptions, publishOptions);
14776
+ this.publisher = new Publisher(basePeerConnectionOptions, publishOptions, {
14777
+ selfSubEnabled: this.selfSubEnabled,
14778
+ });
14421
14779
  }
14422
14780
  this.statsReporter?.stop();
14423
14781
  if (this.statsReportingIntervalInMs > 0) {
@@ -15847,6 +16205,12 @@ class Call {
15847
16205
  get currentUserId() {
15848
16206
  return this.clientStore.connectedUser?.id;
15849
16207
  }
16208
+ /**
16209
+ * A flag indicating whether self-subscription is enabled for the call.
16210
+ */
16211
+ get isSelfSubEnabled() {
16212
+ return this.selfSubEnabled;
16213
+ }
15850
16214
  /**
15851
16215
  * A flag indicating whether the call was created by the current user.
15852
16216
  */
@@ -17036,7 +17400,7 @@ class StreamClient {
17036
17400
  this.getUserAgent = () => {
17037
17401
  if (!this.cachedUserAgent) {
17038
17402
  const { clientAppIdentifier = {} } = this.options;
17039
- const { sdkName = 'js', sdkVersion = "1.51.0", ...extras } = clientAppIdentifier;
17403
+ const { sdkName = 'js', sdkVersion = "1.52.1-beta.0", ...extras } = clientAppIdentifier;
17040
17404
  this.cachedUserAgent = [
17041
17405
  `stream-video-${sdkName}-v${sdkVersion}`,
17042
17406
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
@@ -17762,6 +18126,7 @@ exports.getVideoDevices = getVideoDevices;
17762
18126
  exports.getVideoStream = getVideoStream;
17763
18127
  exports.getWebRTCInfo = getWebRTCInfo;
17764
18128
  exports.hasAudio = hasAudio$1;
18129
+ exports.hasInterruptedTrack = hasInterruptedTrack;
17765
18130
  exports.hasPausedTrack = hasPausedTrack;
17766
18131
  exports.hasScreenShare = hasScreenShare;
17767
18132
  exports.hasScreenShareAudio = hasScreenShareAudio;