@stream-io/video-client 1.21.0 → 1.22.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 (38) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.browser.es.js +142 -133
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +142 -133
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.d.ts +0 -1
  7. package/dist/index.es.js +142 -133
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/src/Call.d.ts +2 -0
  10. package/dist/src/devices/InputMediaDeviceManager.d.ts +3 -3
  11. package/dist/src/devices/SpeakerState.d.ts +3 -1
  12. package/dist/src/devices/devices.d.ts +8 -8
  13. package/dist/src/events/call.d.ts +1 -1
  14. package/dist/src/rtc/BasePeerConnection.d.ts +1 -0
  15. package/dist/src/stats/SfuStatsReporter.d.ts +4 -1
  16. package/dist/src/stats/utils.d.ts +14 -0
  17. package/index.ts +0 -4
  18. package/package.json +10 -10
  19. package/src/Call.ts +5 -2
  20. package/src/devices/CameraManager.ts +27 -23
  21. package/src/devices/InputMediaDeviceManager.ts +8 -5
  22. package/src/devices/MicrophoneManager.ts +1 -1
  23. package/src/devices/ScreenShareManager.ts +2 -2
  24. package/src/devices/SpeakerManager.ts +2 -1
  25. package/src/devices/SpeakerState.ts +6 -3
  26. package/src/devices/__tests__/CameraManager.test.ts +43 -27
  27. package/src/devices/__tests__/MicrophoneManager.test.ts +5 -3
  28. package/src/devices/__tests__/ScreenShareManager.test.ts +5 -1
  29. package/src/devices/__tests__/mocks.ts +2 -3
  30. package/src/devices/devices.ts +38 -16
  31. package/src/events/__tests__/call.test.ts +23 -0
  32. package/src/events/call.ts +12 -1
  33. package/src/rtc/BasePeerConnection.ts +8 -3
  34. package/src/rtc/Publisher.ts +1 -1
  35. package/src/stats/SfuStatsReporter.ts +9 -5
  36. package/src/stats/utils.ts +15 -0
  37. package/dist/src/stats/rtc/mediaDevices.d.ts +0 -2
  38. package/src/stats/rtc/mediaDevices.ts +0 -43
package/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [1.22.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.21.0...@stream-io/video-client-1.22.0) (2025-05-08)
6
+
7
+ ### Features
8
+
9
+ - Expo 53 Swift Config Plugin and React Native 0.79 compatibility ([#1714](https://github.com/GetStream/stream-video-js/issues/1714)) ([380331e](https://github.com/GetStream/stream-video-js/commit/380331e11fd6182c3111413aa25689a669dd3c9c))
10
+
11
+ ### Bug Fixes
12
+
13
+ - graceful handling of LIVE_ENDED CallEnded reason ([#1783](https://github.com/GetStream/stream-video-js/issues/1783)) ([ff54390](https://github.com/GetStream/stream-video-js/commit/ff54390099e10c550b8bbac42658080a65007a30))
14
+ - isolate mediaDevices traces ([#1779](https://github.com/GetStream/stream-video-js/issues/1779)) ([d8623f0](https://github.com/GetStream/stream-video-js/commit/d8623f0b06a6229bff96ea01dd1f2b851b7d3558)), closes [#1765](https://github.com/GetStream/stream-video-js/issues/1765)
15
+ - make camera.flip() work more reliably with older devices ([#1781](https://github.com/GetStream/stream-video-js/issues/1781)) ([9dfbc55](https://github.com/GetStream/stream-video-js/commit/9dfbc556155c1ae9b528b50b140313c4decb024f)), closes [#1679](https://github.com/GetStream/stream-video-js/issues/1679)
16
+ - use scoped locking for PeerConnection events ([#1785](https://github.com/GetStream/stream-video-js/issues/1785)) ([b0f93e8](https://github.com/GetStream/stream-video-js/commit/b0f93e83e70520b527efd94e9192ac7dca031864))
17
+
5
18
  ## [1.21.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.20.2...@stream-io/video-client-1.21.0) (2025-05-02)
6
19
 
7
20
  ### Features
@@ -8,75 +8,6 @@ import { ReplaySubject, combineLatest, BehaviorSubject, map, shareReplay, distin
8
8
  import { UAParser } from 'ua-parser-js';
9
9
  import { parse } from 'sdp-transform';
10
10
 
11
- class Tracer {
12
- constructor(id) {
13
- this.buffer = [];
14
- this.enabled = true;
15
- this.setEnabled = (enabled) => {
16
- if (this.enabled === enabled)
17
- return;
18
- this.enabled = enabled;
19
- this.buffer = [];
20
- };
21
- this.trace = (tag, data) => {
22
- if (!this.enabled)
23
- return;
24
- this.buffer.push([tag, this.id, data, Date.now()]);
25
- };
26
- this.take = () => {
27
- const snapshot = this.buffer;
28
- this.buffer = [];
29
- return {
30
- snapshot,
31
- rollback: () => {
32
- this.buffer.unshift(...snapshot);
33
- },
34
- };
35
- };
36
- this.dispose = () => {
37
- this.buffer = [];
38
- };
39
- this.id = id;
40
- }
41
- }
42
-
43
- const tracer = new Tracer(null);
44
- if (typeof navigator !== 'undefined' &&
45
- typeof navigator.mediaDevices !== 'undefined') {
46
- const dumpStream = (stream) => ({
47
- id: stream.id,
48
- tracks: stream.getTracks().map((track) => ({
49
- id: track.id,
50
- kind: track.kind,
51
- label: track.label,
52
- enabled: track.enabled,
53
- muted: track.muted,
54
- readyState: track.readyState,
55
- })),
56
- });
57
- const trace = tracer.trace;
58
- const target = navigator.mediaDevices;
59
- for (const method of ['getUserMedia', 'getDisplayMedia']) {
60
- const original = target[method];
61
- if (!original)
62
- continue;
63
- let mark = 0;
64
- target[method] = async function tracedMethod(constraints) {
65
- const tag = `navigator.mediaDevices.${method}.${mark++}`;
66
- trace(tag, constraints);
67
- try {
68
- const stream = await original.call(target, constraints);
69
- trace(`${tag}.OnSuccess`, dumpStream(stream));
70
- return stream;
71
- }
72
- catch (err) {
73
- trace(`${tag}.OnFailure`, err.name);
74
- throw err;
75
- }
76
- };
77
- }
78
- }
79
-
80
11
  /* tslint:disable */
81
12
  /**
82
13
  * @export
@@ -5413,6 +5344,20 @@ const flatten = (report) => {
5413
5344
  });
5414
5345
  return stats;
5415
5346
  };
5347
+ /**
5348
+ * Dump the provided MediaStream into a JSON object.
5349
+ */
5350
+ const dumpStream = (stream) => ({
5351
+ id: stream.id,
5352
+ tracks: stream.getTracks().map((track) => ({
5353
+ id: track.id,
5354
+ kind: track.kind,
5355
+ label: track.label,
5356
+ enabled: track.enabled,
5357
+ muted: track.muted,
5358
+ readyState: track.readyState,
5359
+ })),
5360
+ });
5416
5361
  const getSdkSignature = (clientDetails) => {
5417
5362
  const { sdk, ...platform } = clientDetails;
5418
5363
  const sdkName = getSdkName(sdk);
@@ -5701,7 +5646,7 @@ const aggregate = (stats) => {
5701
5646
  return report;
5702
5647
  };
5703
5648
 
5704
- const version = "1.21.0";
5649
+ const version = "1.22.0";
5705
5650
  const [major, minor, patch] = version.split('.');
5706
5651
  let sdkInfo = {
5707
5652
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -5838,7 +5783,7 @@ const getClientDetails = async () => {
5838
5783
  };
5839
5784
 
5840
5785
  class SfuStatsReporter {
5841
- constructor(sfuClient, { options, clientDetails, subscriber, publisher, microphone, camera, state, unifiedSessionId, }) {
5786
+ constructor(sfuClient, { options, clientDetails, subscriber, publisher, microphone, camera, state, tracer, unifiedSessionId, }) {
5842
5787
  this.logger = getLogger(['SfuStatsReporter']);
5843
5788
  this.inputDevices = new Map();
5844
5789
  this.observeDevice = (device, kind) => {
@@ -5904,10 +5849,10 @@ class SfuStatsReporter {
5904
5849
  }
5905
5850
  const subscriberTrace = this.subscriber.tracer?.take();
5906
5851
  const publisherTrace = this.publisher?.tracer?.take();
5907
- const mediaTrace = tracer.take();
5852
+ const tracer = this.tracer.take();
5908
5853
  const sfuTrace = this.sfuClient.getTrace();
5909
5854
  const traces = [
5910
- ...mediaTrace.snapshot,
5855
+ ...tracer.snapshot,
5911
5856
  ...(sfuTrace?.snapshot ?? []),
5912
5857
  ...(publisherTrace?.snapshot ?? []),
5913
5858
  ...(subscriberTrace?.snapshot ?? []),
@@ -5936,7 +5881,7 @@ class SfuStatsReporter {
5936
5881
  catch (err) {
5937
5882
  publisherTrace?.rollback();
5938
5883
  subscriberTrace?.rollback();
5939
- mediaTrace.rollback();
5884
+ tracer.rollback();
5940
5885
  sfuTrace?.rollback();
5941
5886
  throw err;
5942
5887
  }
@@ -5979,6 +5924,7 @@ class SfuStatsReporter {
5979
5924
  this.microphone = microphone;
5980
5925
  this.camera = camera;
5981
5926
  this.state = state;
5927
+ this.tracer = tracer;
5982
5928
  this.unifiedSessionId = unifiedSessionId;
5983
5929
  const { sdk, browser } = clientDetails;
5984
5930
  this.sdkName = getSdkName(sdk);
@@ -6264,6 +6210,38 @@ const getCodecFromStats = (stats, codecId) => {
6264
6210
  });
6265
6211
  };
6266
6212
 
6213
+ class Tracer {
6214
+ constructor(id) {
6215
+ this.buffer = [];
6216
+ this.enabled = true;
6217
+ this.setEnabled = (enabled) => {
6218
+ if (this.enabled === enabled)
6219
+ return;
6220
+ this.enabled = enabled;
6221
+ this.buffer = [];
6222
+ };
6223
+ this.trace = (tag, data) => {
6224
+ if (!this.enabled)
6225
+ return;
6226
+ this.buffer.push([tag, this.id, data, Date.now()]);
6227
+ };
6228
+ this.take = () => {
6229
+ const snapshot = this.buffer;
6230
+ this.buffer = [];
6231
+ return {
6232
+ snapshot,
6233
+ rollback: () => {
6234
+ this.buffer.unshift(...snapshot);
6235
+ },
6236
+ };
6237
+ };
6238
+ this.dispose = () => {
6239
+ this.buffer = [];
6240
+ };
6241
+ this.id = id;
6242
+ }
6243
+ }
6244
+
6267
6245
  /**
6268
6246
  * A base class for the `Publisher` and `Subscriber` classes.
6269
6247
  * @internal
@@ -6277,13 +6255,15 @@ class BasePeerConnection {
6277
6255
  this.isDisposed = false;
6278
6256
  this.trackIdToTrackType = new Map();
6279
6257
  this.subscriptions = [];
6258
+ this.lock = Math.random().toString(36).slice(2);
6280
6259
  /**
6281
6260
  * Handles events synchronously.
6282
6261
  * Consecutive events are queued and executed one after the other.
6283
6262
  */
6284
6263
  this.on = (event, fn) => {
6285
6264
  this.subscriptions.push(this.dispatcher.on(event, (e) => {
6286
- withoutConcurrency(`pc.${event}`, async () => fn(e)).catch((err) => {
6265
+ const lockKey = `pc.${this.lock}.${event}`;
6266
+ withoutConcurrency(lockKey, async () => fn(e)).catch((err) => {
6287
6267
  if (this.isDisposed)
6288
6268
  return;
6289
6269
  this.logger('warn', `Error handling ${event}`, err);
@@ -6442,9 +6422,12 @@ class BasePeerConnection {
6442
6422
  this.pc.addEventListener('connectionstatechange', this.onConnectionStateChange);
6443
6423
  this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType);
6444
6424
  if (enableTracing) {
6445
- const tag = `${logTag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}-${sfuClient.edgeName}`;
6425
+ const tag = `${logTag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}`;
6446
6426
  this.tracer = new Tracer(tag);
6447
- this.tracer.trace('create', connectionConfig);
6427
+ this.tracer.trace('create', {
6428
+ url: sfuClient.edgeName,
6429
+ ...connectionConfig,
6430
+ });
6448
6431
  traceRTCPeerConnection(this.pc, this.tracer.trace);
6449
6432
  }
6450
6433
  }
@@ -6999,7 +6982,7 @@ class Publisher extends BasePeerConnection {
6999
6982
  * @param options the optional offer options to use.
7000
6983
  */
7001
6984
  this.negotiate = async (options) => {
7002
- return withoutConcurrency('publisher.negotiate', async () => {
6985
+ return withoutConcurrency(`publisher.negotiate.${this.lock}`, async () => {
7003
6986
  const offer = await this.pc.createOffer(options);
7004
6987
  const tracks = this.getAnnouncedTracks(offer.sdp);
7005
6988
  if (!tracks.length)
@@ -7907,6 +7890,13 @@ const watchSfuCallEnded = (call) => {
7907
7890
  if (call.state.callingState === CallingState.LEFT)
7908
7891
  return;
7909
7892
  try {
7893
+ if (e.reason === CallEndedReason.LIVE_ENDED) {
7894
+ call.state.setBackstage(true);
7895
+ // don't leave the call if the user has permission to join backstage
7896
+ const { hasPermission } = call.permissionsContext;
7897
+ if (hasPermission(OwnCapability.JOIN_BACKSTAGE))
7898
+ return;
7899
+ }
7910
7900
  // `call.ended` event arrived after the call is already left
7911
7901
  // and all event handlers are detached. We need to manually
7912
7902
  // update the call state to reflect the call has ended.
@@ -9119,15 +9109,25 @@ const getVideoDevices = lazy(() => {
9119
9109
  const getAudioOutputDevices = lazy(() => {
9120
9110
  return merge(getDeviceChangeObserver(), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audiooutput')), shareReplay(1));
9121
9111
  });
9122
- const getStream = async (constraints) => {
9123
- const stream = await navigator.mediaDevices.getUserMedia(constraints);
9124
- if (isFirefox()) {
9125
- // When enumerating devices, Firefox will hide device labels unless there's been
9126
- // an active user media stream on the page. So we force device list updates after
9127
- // every successful getUserMedia call.
9128
- navigator.mediaDevices.dispatchEvent(new Event('devicechange'));
9112
+ let getUserMediaExecId = 0;
9113
+ const getStream = async (constraints, tracer) => {
9114
+ const tag = `navigator.mediaDevices.getUserMedia.${getUserMediaExecId++}.`;
9115
+ try {
9116
+ tracer?.trace(tag, constraints);
9117
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
9118
+ tracer?.trace(`${tag}OnSuccess`, dumpStream(stream));
9119
+ if (isFirefox()) {
9120
+ // When enumerating devices, Firefox will hide device labels unless there's been
9121
+ // an active user media stream on the page. So we force device list updates after
9122
+ // every successful getUserMedia call.
9123
+ navigator.mediaDevices.dispatchEvent(new Event('devicechange'));
9124
+ }
9125
+ return stream;
9126
+ }
9127
+ catch (error) {
9128
+ tracer?.trace(`${tag}OnFailure`, error.name);
9129
+ throw error;
9129
9130
  }
9130
- return stream;
9131
9131
  };
9132
9132
  function isNotFoundOrOverconstrainedError(error) {
9133
9133
  if (!error || typeof error !== 'object') {
@@ -9151,11 +9151,11 @@ function isNotFoundOrOverconstrainedError(error) {
9151
9151
  * Returns an audio media stream that fulfills the given constraints.
9152
9152
  * If no constraints are provided, it uses the browser's default ones.
9153
9153
  *
9154
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9155
9154
  * @param trackConstraints the constraints to use when requesting the stream.
9156
- * @returns the new `MediaStream` fulfilling the given constraints.
9155
+ * @param tracer the tracer to use for tracing the stream creation.
9156
+ * @returns a new `MediaStream` fulfilling the given constraints.
9157
9157
  */
9158
- const getAudioStream = async (trackConstraints) => {
9158
+ const getAudioStream = async (trackConstraints, tracer) => {
9159
9159
  const constraints = {
9160
9160
  audio: {
9161
9161
  ...audioDeviceConstraints.audio,
@@ -9167,7 +9167,7 @@ const getAudioStream = async (trackConstraints) => {
9167
9167
  throwOnNotAllowed: true,
9168
9168
  forcePrompt: true,
9169
9169
  });
9170
- return await getStream(constraints);
9170
+ return await getStream(constraints, tracer);
9171
9171
  }
9172
9172
  catch (error) {
9173
9173
  if (isNotFoundOrOverconstrainedError(error) && trackConstraints?.deviceId) {
@@ -9187,11 +9187,11 @@ const getAudioStream = async (trackConstraints) => {
9187
9187
  * Returns a video media stream that fulfills the given constraints.
9188
9188
  * If no constraints are provided, it uses the browser's default ones.
9189
9189
  *
9190
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9191
9190
  * @param trackConstraints the constraints to use when requesting the stream.
9191
+ * @param tracer the tracer to use for tracing the stream creation.
9192
9192
  * @returns a new `MediaStream` fulfilling the given constraints.
9193
9193
  */
9194
- const getVideoStream = async (trackConstraints) => {
9194
+ const getVideoStream = async (trackConstraints, tracer) => {
9195
9195
  const constraints = {
9196
9196
  video: {
9197
9197
  ...videoDeviceConstraints.video,
@@ -9203,7 +9203,7 @@ const getVideoStream = async (trackConstraints) => {
9203
9203
  throwOnNotAllowed: true,
9204
9204
  forcePrompt: true,
9205
9205
  });
9206
- return await getStream(constraints);
9206
+ return await getStream(constraints, tracer);
9207
9207
  }
9208
9208
  catch (error) {
9209
9209
  if (isNotFoundOrOverconstrainedError(error) && trackConstraints?.deviceId) {
@@ -9219,19 +9219,21 @@ const getVideoStream = async (trackConstraints) => {
9219
9219
  throw error;
9220
9220
  }
9221
9221
  };
9222
+ let getDisplayMediaExecId = 0;
9222
9223
  /**
9223
9224
  * Prompts the user for a permission to share a screen.
9224
9225
  * If the user grants the permission, a screen sharing stream is returned. Throws otherwise.
9225
9226
  *
9226
9227
  * The callers of this API are responsible to handle the possible errors.
9227
9228
  *
9228
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9229
- *
9230
9229
  * @param options any additional options to pass to the [`getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) API.
9230
+ * @param tracer the tracer to use for tracing the stream creation.
9231
9231
  */
9232
- const getScreenShareStream = async (options) => {
9232
+ const getScreenShareStream = async (options, tracer) => {
9233
+ const tag = `navigator.mediaDevices.getDisplayMedia.${getDisplayMediaExecId++}.`;
9233
9234
  try {
9234
- return await navigator.mediaDevices.getDisplayMedia({
9235
+ tracer?.trace(tag, options);
9236
+ const stream = await navigator.mediaDevices.getDisplayMedia({
9235
9237
  video: true,
9236
9238
  audio: {
9237
9239
  channelCount: {
@@ -9245,8 +9247,11 @@ const getScreenShareStream = async (options) => {
9245
9247
  systemAudio: 'include',
9246
9248
  ...options,
9247
9249
  });
9250
+ tracer?.trace(`${tag}OnSuccess`, dumpStream(stream));
9251
+ return stream;
9248
9252
  }
9249
9253
  catch (e) {
9254
+ tracer?.trace(`${tag}OnFailure`, e.name);
9250
9255
  getLogger(['devices'])('error', 'Failed to get screen share stream', e);
9251
9256
  throw e;
9252
9257
  }
@@ -9284,9 +9289,6 @@ const isMobile = () => /Mobi/i.test(navigator.userAgent);
9284
9289
 
9285
9290
  class InputMediaDeviceManager {
9286
9291
  constructor(call, state, trackType) {
9287
- this.call = call;
9288
- this.state = state;
9289
- this.trackType = trackType;
9290
9292
  /**
9291
9293
  * if true, stops the media stream when call is left
9292
9294
  */
@@ -9304,6 +9306,9 @@ class InputMediaDeviceManager {
9304
9306
  this.dispose = () => {
9305
9307
  this.subscriptions.forEach((s) => s());
9306
9308
  };
9309
+ this.call = call;
9310
+ this.state = state;
9311
+ this.trackType = trackType;
9307
9312
  this.logger = getLogger([`${TrackType[trackType].toLowerCase()} manager`]);
9308
9313
  if (deviceIds$ &&
9309
9314
  !isReactNative() &&
@@ -9900,32 +9905,32 @@ class CameraManager extends InputMediaDeviceManager {
9900
9905
  * @param direction the direction of the camera to select.
9901
9906
  */
9902
9907
  async selectDirection(direction) {
9903
- if (this.isDirectionSupportedByDevice()) {
9904
- if (isReactNative()) {
9905
- const videoTrack = this.getTracks()[0];
9906
- if (!videoTrack) {
9907
- this.logger('warn', 'No video track found to do direction selection');
9908
- return;
9909
- }
9910
- await videoTrack.applyConstraints({
9911
- facingMode: direction === 'front' ? 'user' : 'environment',
9912
- });
9913
- this.state.setDirection(direction);
9914
- this.state.setDevice(undefined);
9915
- }
9916
- else {
9917
- // web mobile
9918
- this.state.setDirection(direction);
9919
- // Providing both device id and direction doesn't work, so we deselect the device
9920
- this.state.setDevice(undefined);
9921
- this.getTracks().forEach((track) => {
9922
- track.stop();
9923
- });
9908
+ if (!this.isDirectionSupportedByDevice()) {
9909
+ this.logger('warn', 'Setting direction is not supported on this device');
9910
+ return;
9911
+ }
9912
+ // providing both device id and direction doesn't work, so we deselect the device
9913
+ this.state.setDirection(direction);
9914
+ this.state.setDevice(undefined);
9915
+ if (isReactNative()) {
9916
+ const videoTrack = this.getTracks()[0];
9917
+ await videoTrack?.applyConstraints({
9918
+ facingMode: direction === 'front' ? 'user' : 'environment',
9919
+ });
9920
+ return;
9921
+ }
9922
+ this.getTracks().forEach((track) => track.stop());
9923
+ try {
9924
+ await this.unmuteStream();
9925
+ }
9926
+ catch (error) {
9927
+ if (error instanceof Error && error.name === 'NotReadableError') {
9928
+ // the camera is already in use, and the device can't use it unless it's released.
9929
+ // in that case, we need to stop the stream and start it again.
9930
+ await this.muteStream();
9924
9931
  await this.unmuteStream();
9925
9932
  }
9926
- }
9927
- else {
9928
- this.logger('warn', 'Camera direction ignored for desktop devices');
9933
+ throw error;
9929
9934
  }
9930
9935
  }
9931
9936
  /**
@@ -10010,7 +10015,7 @@ class CameraManager extends InputMediaDeviceManager {
10010
10015
  constraints.facingMode =
10011
10016
  this.state.direction === 'front' ? 'user' : 'environment';
10012
10017
  }
10013
- return getVideoStream(constraints);
10018
+ return getVideoStream(constraints, this.call.tracer);
10014
10019
  }
10015
10020
  }
10016
10021
 
@@ -10421,7 +10426,7 @@ class MicrophoneManager extends InputMediaDeviceManager {
10421
10426
  return getAudioDevices();
10422
10427
  }
10423
10428
  getStream(constraints) {
10424
- return getAudioStream(constraints);
10429
+ return getAudioStream(constraints, this.call.tracer);
10425
10430
  }
10426
10431
  async startSpeakingWhileMutedDetection(deviceId) {
10427
10432
  await withoutConcurrency(this.soundDetectorConcurrencyTag, async () => {
@@ -10564,7 +10569,7 @@ class ScreenShareManager extends InputMediaDeviceManager {
10564
10569
  if (!this.state.audioEnabled) {
10565
10570
  constraints.audio = false;
10566
10571
  }
10567
- return getScreenShareStream(constraints);
10572
+ return getScreenShareStream(constraints, this.call.tracer);
10568
10573
  }
10569
10574
  async stopPublishStream() {
10570
10575
  return this.call.stopPublish(TrackType.SCREEN_SHARE, TrackType.SCREEN_SHARE_AUDIO);
@@ -10573,18 +10578,19 @@ class ScreenShareManager extends InputMediaDeviceManager {
10573
10578
  * Overrides the default `select` method to throw an error.
10574
10579
  */
10575
10580
  async select() {
10576
- throw new Error('This method is not supported in for Screen Share');
10581
+ throw new Error('Not supported');
10577
10582
  }
10578
10583
  }
10579
10584
 
10580
10585
  class SpeakerState {
10581
- constructor() {
10586
+ constructor(tracer) {
10582
10587
  this.selectedDeviceSubject = new BehaviorSubject('');
10583
10588
  this.volumeSubject = new BehaviorSubject(1);
10584
10589
  /**
10585
10590
  * [Tells if the browser supports audio output change on 'audio' elements](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId).
10586
10591
  */
10587
10592
  this.isDeviceSelectionSupported = checkIfAudioOutputChangeSupported();
10593
+ this.tracer = tracer;
10588
10594
  this.selectedDevice$ = this.selectedDeviceSubject
10589
10595
  .asObservable()
10590
10596
  .pipe(distinctUntilChanged());
@@ -10614,7 +10620,7 @@ class SpeakerState {
10614
10620
  */
10615
10621
  setDevice(deviceId) {
10616
10622
  setCurrentValue(this.selectedDeviceSubject, deviceId);
10617
- tracer.trace('navigator.mediaDevices.setSinkId', deviceId);
10623
+ this.tracer.trace('navigator.mediaDevices.setSinkId', deviceId);
10618
10624
  }
10619
10625
  /**
10620
10626
  * @internal
@@ -10627,7 +10633,6 @@ class SpeakerState {
10627
10633
 
10628
10634
  class SpeakerManager {
10629
10635
  constructor(call) {
10630
- this.state = new SpeakerState();
10631
10636
  this.subscriptions = [];
10632
10637
  /**
10633
10638
  * Disposes the manager.
@@ -10638,6 +10643,7 @@ class SpeakerManager {
10638
10643
  this.subscriptions.forEach((s) => s.unsubscribe());
10639
10644
  };
10640
10645
  this.call = call;
10646
+ this.state = new SpeakerState(call.tracer);
10641
10647
  if (deviceIds$ && !isReactNative()) {
10642
10648
  this.subscriptions.push(combineLatest([deviceIds$, this.state.selectedDevice$]).subscribe(([devices, deviceId]) => {
10643
10649
  if (!deviceId) {
@@ -10731,6 +10737,7 @@ class Call {
10731
10737
  * The permissions context of this call.
10732
10738
  */
10733
10739
  this.permissionsContext = new PermissionsContext();
10740
+ this.tracer = new Tracer(null);
10734
10741
  /**
10735
10742
  * The event dispatcher instance dedicated to this Call instance.
10736
10743
  * @private
@@ -11459,7 +11466,6 @@ class Call {
11459
11466
  },
11460
11467
  });
11461
11468
  }
11462
- tracer.setEnabled(enableTracing);
11463
11469
  this.statsReporter?.stop();
11464
11470
  this.statsReporter = createStatsReporter({
11465
11471
  subscriber: this.subscriber,
@@ -11467,6 +11473,7 @@ class Call {
11467
11473
  state: this.state,
11468
11474
  datacenter: sfuClient.edgeName,
11469
11475
  });
11476
+ this.tracer.setEnabled(enableTracing);
11470
11477
  this.sfuStatsReporter?.stop();
11471
11478
  if (statsOptions?.reporting_interval_ms > 0) {
11472
11479
  this.unifiedSessionId ?? (this.unifiedSessionId = sfuClient.sessionId);
@@ -11478,6 +11485,7 @@ class Call {
11478
11485
  microphone: this.microphone,
11479
11486
  camera: this.camera,
11480
11487
  state: this.state,
11488
+ tracer: this.tracer,
11481
11489
  unifiedSessionId: this.unifiedSessionId,
11482
11490
  });
11483
11491
  this.sfuStatsReporter.start();
@@ -11711,6 +11719,7 @@ class Call {
11711
11719
  }
11712
11720
  });
11713
11721
  const unregisterNetworkChanged = this.streamClient.on('network.changed', (e) => {
11722
+ this.tracer.trace('network.changed', e);
11714
11723
  if (!e.online) {
11715
11724
  this.logger('debug', '[Reconnect] Going offline');
11716
11725
  if (!this.hasJoinedOnce)
@@ -13714,7 +13723,7 @@ class StreamClient {
13714
13723
  this.getUserAgent = () => {
13715
13724
  if (!this.cachedUserAgent) {
13716
13725
  const { clientAppIdentifier = {} } = this.options;
13717
- const { sdkName = 'js', sdkVersion = "1.21.0", ...extras } = clientAppIdentifier;
13726
+ const { sdkName = 'js', sdkVersion = "1.22.0", ...extras } = clientAppIdentifier;
13718
13727
  this.cachedUserAgent = [
13719
13728
  `stream-video-${sdkName}-v${sdkVersion}`,
13720
13729
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),