@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
@@ -3,6 +3,7 @@ import { CallState } from './store';
3
3
  import type { AcceptCallResponse, BlockUserResponse, CallRingEvent, CallSettingsResponse, CollectUserFeedbackRequest, CollectUserFeedbackResponse, DeleteCallRequest, DeleteCallResponse, EndCallResponse, GetCallReportResponse, GetCallResponse, GetOrCreateCallRequest, GetOrCreateCallResponse, GoLiveRequest, GoLiveResponse, JoinCallResponse, ListRecordingsResponse, ListTranscriptionsResponse, MuteUsersResponse, PinRequest, PinResponse, QueryCallMembersRequest, QueryCallMembersResponse, RejectCallResponse, RequestPermissionRequest, RequestPermissionResponse, SendCallEventResponse, SendReactionRequest, SendReactionResponse, StartClosedCaptionsRequest, StartClosedCaptionsResponse, StartFrameRecordingRequest, StartFrameRecordingResponse, StartHLSBroadcastingResponse, StartRecordingRequest, StartRecordingResponse, StartRTMPBroadcastsRequest, StartRTMPBroadcastsResponse, StartTranscriptionRequest, StartTranscriptionResponse, StopAllRTMPBroadcastsResponse, StopClosedCaptionsRequest, StopClosedCaptionsResponse, StopFrameRecordingResponse, StopHLSBroadcastingResponse, StopLiveRequest, StopLiveResponse, StopRecordingResponse, StopRTMPBroadcastsResponse, StopTranscriptionResponse, UnblockUserResponse, UnpinRequest, UnpinResponse, UpdateCallMembersRequest, UpdateCallMembersResponse, UpdateCallRequest, UpdateCallResponse, UpdateUserPermissionsRequest, UpdateUserPermissionsResponse, VideoDimension } from './gen/coordinator';
4
4
  import { AudioTrackType, CallConstructor, CallLeaveOptions, ClientPublishOptions, ClosedCaptionsSettings, JoinCallData, TrackMuteType, VideoTrackType } from './types';
5
5
  import { TrackType } from './gen/video/sfu/models/models';
6
+ import { Tracer } from './stats';
6
7
  import { DynascaleManager } from './helpers/DynascaleManager';
7
8
  import { PermissionsContext } from './permissions';
8
9
  import { StreamClient } from './coordinator/connection/client';
@@ -64,6 +65,7 @@ export declare class Call {
64
65
  * The permissions context of this call.
65
66
  */
66
67
  readonly permissionsContext: PermissionsContext;
68
+ readonly tracer: Tracer;
67
69
  readonly logger: Logger;
68
70
  /**
69
71
  * The event dispatcher instance dedicated to this Call instance.
@@ -5,14 +5,14 @@ import { Logger } from '../coordinator/connection/types';
5
5
  import { TrackType } from '../gen/video/sfu/models/models';
6
6
  import { MediaStreamFilter, MediaStreamFilterRegistrationResult } from './filters';
7
7
  export declare abstract class InputMediaDeviceManager<T extends InputMediaDeviceManagerState<C>, C = MediaTrackConstraints> {
8
- protected readonly call: Call;
9
- readonly state: T;
10
- protected readonly trackType: TrackType;
11
8
  /**
12
9
  * if true, stops the media stream when call is left
13
10
  */
14
11
  stopOnLeave: boolean;
15
12
  logger: Logger;
13
+ state: T;
14
+ protected readonly call: Call;
15
+ protected readonly trackType: TrackType;
16
16
  protected subscriptions: Function[];
17
17
  private isTrackStoppedDueToTrackEnd;
18
18
  private filters;
@@ -1,4 +1,5 @@
1
1
  import { BehaviorSubject, Observable } from 'rxjs';
2
+ import { Tracer } from '../stats';
2
3
  export declare class SpeakerState {
3
4
  protected selectedDeviceSubject: BehaviorSubject<string>;
4
5
  protected volumeSubject: BehaviorSubject<number>;
@@ -18,7 +19,8 @@ export declare class SpeakerState {
18
19
  * Note: this feature is not supported in React Native
19
20
  */
20
21
  volume$: Observable<number>;
21
- constructor();
22
+ private tracer;
23
+ constructor(tracer: Tracer);
22
24
  /**
23
25
  * The currently selected device
24
26
  *
@@ -1,4 +1,5 @@
1
1
  import { BrowserPermission } from './BrowserPermission';
2
+ import { Tracer } from '../stats';
2
3
  /**
3
4
  * Tells if the browser supports audio output change on 'audio' elements,
4
5
  * see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId.
@@ -39,31 +40,30 @@ export declare const getAudioOutputDevices: () => import("rxjs").Observable<Medi
39
40
  * Returns an audio media stream that fulfills the given constraints.
40
41
  * If no constraints are provided, it uses the browser's default ones.
41
42
  *
42
- * @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.
43
43
  * @param trackConstraints the constraints to use when requesting the stream.
44
- * @returns the new `MediaStream` fulfilling the given constraints.
44
+ * @param tracer the tracer to use for tracing the stream creation.
45
+ * @returns a new `MediaStream` fulfilling the given constraints.
45
46
  */
46
- export declare const getAudioStream: (trackConstraints?: MediaTrackConstraints) => Promise<MediaStream>;
47
+ export declare const getAudioStream: (trackConstraints?: MediaTrackConstraints, tracer?: Tracer) => Promise<MediaStream>;
47
48
  /**
48
49
  * Returns a video media stream that fulfills the given constraints.
49
50
  * If no constraints are provided, it uses the browser's default ones.
50
51
  *
51
- * @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.
52
52
  * @param trackConstraints the constraints to use when requesting the stream.
53
+ * @param tracer the tracer to use for tracing the stream creation.
53
54
  * @returns a new `MediaStream` fulfilling the given constraints.
54
55
  */
55
- export declare const getVideoStream: (trackConstraints?: MediaTrackConstraints) => Promise<MediaStream>;
56
+ export declare const getVideoStream: (trackConstraints?: MediaTrackConstraints, tracer?: Tracer) => Promise<MediaStream>;
56
57
  /**
57
58
  * Prompts the user for a permission to share a screen.
58
59
  * If the user grants the permission, a screen sharing stream is returned. Throws otherwise.
59
60
  *
60
61
  * The callers of this API are responsible to handle the possible errors.
61
62
  *
62
- * @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.
63
- *
64
63
  * @param options any additional options to pass to the [`getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) API.
64
+ * @param tracer the tracer to use for tracing the stream creation.
65
65
  */
66
- export declare const getScreenShareStream: (options?: DisplayMediaStreamOptions) => Promise<MediaStream>;
66
+ export declare const getScreenShareStream: (options?: DisplayMediaStreamOptions, tracer?: Tracer | undefined) => Promise<MediaStream>;
67
67
  export declare const deviceIds$: import("rxjs").Observable<MediaDeviceInfo[]> | undefined;
68
68
  /**
69
69
  * Deactivates MediaStream (stops and removes tracks) to be later garbage collected
@@ -1,5 +1,5 @@
1
1
  import { Call } from '../Call';
2
- import type { CallAcceptedEvent, CallRejectedEvent } from '../gen/coordinator';
2
+ import { CallAcceptedEvent, CallRejectedEvent } from '../gen/coordinator';
3
3
  /**
4
4
  * Event handler that watched the delivery of `call.accepted`.
5
5
  * Once the event is received, the call is joined.
@@ -32,6 +32,7 @@ export declare abstract class BasePeerConnection {
32
32
  readonly stats: StatsTracer;
33
33
  private readonly subscriptions;
34
34
  private unsubscribeIceTrickle?;
35
+ protected readonly lock: string;
35
36
  /**
36
37
  * Constructs a new `BasePeerConnection` instance.
37
38
  */
@@ -1,6 +1,7 @@
1
1
  import { StreamSfuClient } from '../StreamSfuClient';
2
2
  import { StatsOptions } from '../gen/coordinator';
3
3
  import { Publisher, Subscriber } from '../rtc';
4
+ import { Tracer } from './rtc';
4
5
  import { ClientDetails, WebsocketReconnectStrategy } from '../gen/video/sfu/models/models';
5
6
  import { CameraManager, MicrophoneManager } from '../devices';
6
7
  import { CallState } from '../store';
@@ -12,6 +13,7 @@ export type SfuStatsReporterOptions = {
12
13
  microphone: MicrophoneManager;
13
14
  camera: CameraManager;
14
15
  state: CallState;
16
+ tracer: Tracer;
15
17
  unifiedSessionId: string;
16
18
  };
17
19
  export declare class SfuStatsReporter {
@@ -23,6 +25,7 @@ export declare class SfuStatsReporter {
23
25
  private readonly microphone;
24
26
  private readonly camera;
25
27
  private readonly state;
28
+ private readonly tracer;
26
29
  private readonly unifiedSessionId;
27
30
  private intervalId;
28
31
  private timeoutId;
@@ -32,7 +35,7 @@ export declare class SfuStatsReporter {
32
35
  private readonly sdkVersion;
33
36
  private readonly webRTCVersion;
34
37
  private readonly inputDevices;
35
- constructor(sfuClient: StreamSfuClient, { options, clientDetails, subscriber, publisher, microphone, camera, state, unifiedSessionId, }: SfuStatsReporterOptions);
38
+ constructor(sfuClient: StreamSfuClient, { options, clientDetails, subscriber, publisher, microphone, camera, state, tracer, unifiedSessionId, }: SfuStatsReporterOptions);
36
39
  private observeDevice;
37
40
  sendConnectionTime: (connectionTimeSeconds: number) => void;
38
41
  sendReconnectionTime: (strategy: WebsocketReconnectStrategy, timeSeconds: number) => void;
@@ -5,6 +5,20 @@ import { ClientDetails, Sdk } from '../gen/video/sfu/models/models';
5
5
  * @param report the report to flatten.
6
6
  */
7
7
  export declare const flatten: (report: RTCStatsReport) => RTCStats[];
8
+ /**
9
+ * Dump the provided MediaStream into a JSON object.
10
+ */
11
+ export declare const dumpStream: (stream: MediaStream) => {
12
+ id: string;
13
+ tracks: {
14
+ id: string;
15
+ kind: string;
16
+ label: string;
17
+ enabled: boolean;
18
+ muted: boolean;
19
+ readyState: MediaStreamTrackState;
20
+ }[];
21
+ };
8
22
  export declare const getSdkSignature: (clientDetails: ClientDetails) => {
9
23
  os?: import("../gen/video/sfu/models/models").OS;
10
24
  browser?: import("../gen/video/sfu/models/models").Browser;
package/index.ts CHANGED
@@ -1,9 +1,5 @@
1
1
  import 'webrtc-adapter';
2
2
 
3
- // side effect: we patch the mediaDevices APIs here
4
- // so we can intercept invocations and collect statistics
5
- import './src/stats/rtc/mediaDevices';
6
-
7
3
  export * from './src/gen/coordinator';
8
4
  export * from './src/coordinator/connection/types';
9
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-client",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "main": "dist/index.cjs.js",
5
5
  "module": "dist/index.es.js",
6
6
  "browser": "dist/index.browser.es.js",
@@ -40,19 +40,19 @@
40
40
  "@openapitools/openapi-generator-cli": "^2.13.4",
41
41
  "@rollup/plugin-replace": "^6.0.2",
42
42
  "@rollup/plugin-typescript": "^12.1.2",
43
- "@stream-io/audio-filters-web": "^0.2.3",
44
- "@stream-io/node-sdk": "^0.4.19",
43
+ "@stream-io/audio-filters-web": "^0.3.0",
44
+ "@stream-io/node-sdk": "^0.4.24",
45
45
  "@types/sdp-transform": "^2.4.9",
46
46
  "@types/ua-parser-js": "^0.7.39",
47
- "@vitest/coverage-v8": "^3.0.9",
48
- "dotenv": "^16.4.7",
47
+ "@vitest/coverage-v8": "^3.1.3",
48
+ "dotenv": "^16.5.0",
49
49
  "happy-dom": "^11.0.2",
50
50
  "prettier": "^3.5.3",
51
51
  "rimraf": "^6.0.1",
52
- "rollup": "^4.36.0",
53
- "typescript": "^5.8.2",
54
- "vite": "^6.2.2",
55
- "vitest": "^3.0.9",
56
- "vitest-mock-extended": "^3.0.1"
52
+ "rollup": "^4.40.2",
53
+ "typescript": "^5.8.3",
54
+ "vite": "^6.3.5",
55
+ "vitest": "^3.1.3",
56
+ "vitest-mock-extended": "^3.1.0"
57
57
  }
58
58
  }
package/src/Call.ts CHANGED
@@ -120,8 +120,8 @@ import {
120
120
  getSdkSignature,
121
121
  SfuStatsReporter,
122
122
  StatsReporter,
123
+ Tracer,
123
124
  } from './stats';
124
- import { tracer as mediaStatsTracer } from './stats/rtc/mediaDevices';
125
125
  import { DynascaleManager } from './helpers/DynascaleManager';
126
126
  import { PermissionsContext } from './permissions';
127
127
  import { CallTypes } from './CallType';
@@ -220,6 +220,7 @@ export class Call {
220
220
  * The permissions context of this call.
221
221
  */
222
222
  readonly permissionsContext = new PermissionsContext();
223
+ readonly tracer = new Tracer(null);
223
224
  readonly logger: Logger;
224
225
 
225
226
  /**
@@ -1225,7 +1226,6 @@ export class Call {
1225
1226
  });
1226
1227
  }
1227
1228
 
1228
- mediaStatsTracer.setEnabled(enableTracing);
1229
1229
  this.statsReporter?.stop();
1230
1230
  this.statsReporter = createStatsReporter({
1231
1231
  subscriber: this.subscriber,
@@ -1234,6 +1234,7 @@ export class Call {
1234
1234
  datacenter: sfuClient.edgeName,
1235
1235
  });
1236
1236
 
1237
+ this.tracer.setEnabled(enableTracing);
1237
1238
  this.sfuStatsReporter?.stop();
1238
1239
  if (statsOptions?.reporting_interval_ms > 0) {
1239
1240
  this.unifiedSessionId ??= sfuClient.sessionId;
@@ -1245,6 +1246,7 @@ export class Call {
1245
1246
  microphone: this.microphone,
1246
1247
  camera: this.camera,
1247
1248
  state: this.state,
1249
+ tracer: this.tracer,
1248
1250
  unifiedSessionId: this.unifiedSessionId,
1249
1251
  });
1250
1252
  this.sfuStatsReporter.start();
@@ -1548,6 +1550,7 @@ export class Call {
1548
1550
  const unregisterNetworkChanged = this.streamClient.on(
1549
1551
  'network.changed',
1550
1552
  (e) => {
1553
+ this.tracer.trace('network.changed', e);
1551
1554
  if (!e.online) {
1552
1555
  this.logger('debug', '[Reconnect] Going offline');
1553
1556
  if (!this.hasJoinedOnce) return;
@@ -33,30 +33,34 @@ export class CameraManager extends InputMediaDeviceManager<CameraManagerState> {
33
33
  * @param direction the direction of the camera to select.
34
34
  */
35
35
  async selectDirection(direction: Exclude<CameraDirection, undefined>) {
36
- if (this.isDirectionSupportedByDevice()) {
37
- if (isReactNative()) {
38
- const videoTrack = this.getTracks()[0];
39
- if (!videoTrack) {
40
- this.logger('warn', 'No video track found to do direction selection');
41
- return;
42
- }
43
- await videoTrack.applyConstraints({
44
- facingMode: direction === 'front' ? 'user' : 'environment',
45
- });
46
- this.state.setDirection(direction);
47
- this.state.setDevice(undefined);
48
- } else {
49
- // web mobile
50
- this.state.setDirection(direction);
51
- // Providing both device id and direction doesn't work, so we deselect the device
52
- this.state.setDevice(undefined);
53
- this.getTracks().forEach((track) => {
54
- track.stop();
55
- });
36
+ if (!this.isDirectionSupportedByDevice()) {
37
+ this.logger('warn', 'Setting direction is not supported on this device');
38
+ return;
39
+ }
40
+
41
+ // providing both device id and direction doesn't work, so we deselect the device
42
+ this.state.setDirection(direction);
43
+ this.state.setDevice(undefined);
44
+
45
+ if (isReactNative()) {
46
+ const videoTrack = this.getTracks()[0] as MediaStreamTrack | undefined;
47
+ await videoTrack?.applyConstraints({
48
+ facingMode: direction === 'front' ? 'user' : 'environment',
49
+ });
50
+ return;
51
+ }
52
+
53
+ this.getTracks().forEach((track) => track.stop());
54
+ try {
55
+ await this.unmuteStream();
56
+ } catch (error) {
57
+ if (error instanceof Error && error.name === 'NotReadableError') {
58
+ // the camera is already in use, and the device can't use it unless it's released.
59
+ // in that case, we need to stop the stream and start it again.
60
+ await this.muteStream();
56
61
  await this.unmuteStream();
57
62
  }
58
- } else {
59
- this.logger('warn', 'Camera direction ignored for desktop devices');
63
+ throw error;
60
64
  }
61
65
  }
62
66
 
@@ -158,6 +162,6 @@ export class CameraManager extends InputMediaDeviceManager<CameraManagerState> {
158
162
  constraints.facingMode =
159
163
  this.state.direction === 'front' ? 'user' : 'environment';
160
164
  }
161
- return getVideoStream(constraints);
165
+ return getVideoStream(constraints, this.call.tracer);
162
166
  }
163
167
  }
@@ -30,6 +30,10 @@ export abstract class InputMediaDeviceManager<
30
30
  stopOnLeave = true;
31
31
  logger: Logger;
32
32
 
33
+ state: T;
34
+
35
+ protected readonly call: Call;
36
+ protected readonly trackType: TrackType;
33
37
  protected subscriptions: Function[] = [];
34
38
  private isTrackStoppedDueToTrackEnd = false;
35
39
  private filters: MediaStreamFilterEntry[] = [];
@@ -38,11 +42,10 @@ export abstract class InputMediaDeviceManager<
38
42
  'filterRegistrationConcurrencyTag',
39
43
  );
40
44
 
41
- protected constructor(
42
- protected readonly call: Call,
43
- public readonly state: T,
44
- protected readonly trackType: TrackType,
45
- ) {
45
+ protected constructor(call: Call, state: T, trackType: TrackType) {
46
+ this.call = call;
47
+ this.state = state;
48
+ this.trackType = trackType;
46
49
  this.logger = getLogger([`${TrackType[trackType].toLowerCase()} manager`]);
47
50
  if (
48
51
  deviceIds$ &&
@@ -235,7 +235,7 @@ export class MicrophoneManager extends InputMediaDeviceManager<MicrophoneManager
235
235
  protected getStream(
236
236
  constraints: MediaTrackConstraints,
237
237
  ): Promise<MediaStream> {
238
- return getAudioStream(constraints);
238
+ return getAudioStream(constraints, this.call.tracer);
239
239
  }
240
240
 
241
241
  private async startSpeakingWhileMutedDetection(deviceId?: string) {
@@ -76,7 +76,7 @@ export class ScreenShareManager extends InputMediaDeviceManager<
76
76
  if (!this.state.audioEnabled) {
77
77
  constraints.audio = false;
78
78
  }
79
- return getScreenShareStream(constraints);
79
+ return getScreenShareStream(constraints, this.call.tracer);
80
80
  }
81
81
 
82
82
  protected async stopPublishStream(): Promise<void> {
@@ -90,6 +90,6 @@ export class ScreenShareManager extends InputMediaDeviceManager<
90
90
  * Overrides the default `select` method to throw an error.
91
91
  */
92
92
  async select(): Promise<void> {
93
- throw new Error('This method is not supported in for Screen Share');
93
+ throw new Error('Not supported');
94
94
  }
95
95
  }
@@ -5,12 +5,13 @@ import { SpeakerState } from './SpeakerState';
5
5
  import { deviceIds$, getAudioOutputDevices } from './devices';
6
6
 
7
7
  export class SpeakerManager {
8
- public readonly state = new SpeakerState();
8
+ readonly state: SpeakerState;
9
9
  private subscriptions: Subscription[] = [];
10
10
  private readonly call: Call;
11
11
 
12
12
  constructor(call: Call) {
13
13
  this.call = call;
14
+ this.state = new SpeakerState(call.tracer);
14
15
  if (deviceIds$ && !isReactNative()) {
15
16
  this.subscriptions.push(
16
17
  combineLatest([deviceIds$!, this.state.selectedDevice$]).subscribe(
@@ -1,7 +1,7 @@
1
1
  import { BehaviorSubject, distinctUntilChanged, Observable } from 'rxjs';
2
2
  import { RxUtils } from '../store';
3
3
  import { checkIfAudioOutputChangeSupported } from './devices';
4
- import { tracer as mediaStatsTracer } from '../stats/rtc/mediaDevices';
4
+ import { Tracer } from '../stats';
5
5
 
6
6
  export class SpeakerState {
7
7
  protected selectedDeviceSubject = new BehaviorSubject<string>('');
@@ -25,7 +25,10 @@ export class SpeakerState {
25
25
  */
26
26
  volume$: Observable<number>;
27
27
 
28
- constructor() {
28
+ private tracer: Tracer;
29
+
30
+ constructor(tracer: Tracer) {
31
+ this.tracer = tracer;
29
32
  this.selectedDevice$ = this.selectedDeviceSubject
30
33
  .asObservable()
31
34
  .pipe(distinctUntilChanged());
@@ -58,7 +61,7 @@ export class SpeakerState {
58
61
  */
59
62
  setDevice(deviceId: string) {
60
63
  RxUtils.setCurrentValue(this.selectedDeviceSubject, deviceId);
61
- mediaStatsTracer.trace('navigator.mediaDevices.setSinkId', deviceId);
64
+ this.tracer.trace('navigator.mediaDevices.setSinkId', deviceId);
62
65
  }
63
66
 
64
67
  /**
@@ -14,6 +14,7 @@ import { TrackType } from '../../gen/video/sfu/models/models';
14
14
  import { CameraManager } from '../CameraManager';
15
15
  import { of } from 'rxjs';
16
16
  import { PermissionsContext } from '../../permissions';
17
+ import { Tracer } from '../../stats';
17
18
 
18
19
  const getVideoStream = vi.hoisted(() =>
19
20
  vi.fn(() => Promise.resolve(mockVideoStream())),
@@ -78,11 +79,14 @@ describe('CameraManager', () => {
78
79
  it('get stream', async () => {
79
80
  await manager.enable();
80
81
 
81
- expect(getVideoStream).toHaveBeenCalledWith({
82
- deviceId: undefined,
83
- width: 1280,
84
- height: 720,
85
- });
82
+ expect(getVideoStream).toHaveBeenCalledWith(
83
+ {
84
+ deviceId: undefined,
85
+ width: 1280,
86
+ height: 720,
87
+ },
88
+ expect.any(Tracer),
89
+ );
86
90
  });
87
91
 
88
92
  it('should get device id from stream', async () => {
@@ -133,29 +137,38 @@ describe('CameraManager', () => {
133
137
 
134
138
  await manager.enable();
135
139
 
136
- expect(getVideoStream).toHaveBeenCalledWith({
137
- deviceId: undefined,
138
- width: 1280,
139
- height: 720,
140
- });
140
+ expect(getVideoStream).toHaveBeenCalledWith(
141
+ {
142
+ deviceId: undefined,
143
+ width: 1280,
144
+ height: 720,
145
+ },
146
+ expect.any(Tracer),
147
+ );
141
148
 
142
149
  await manager.selectDirection('front');
143
150
 
144
- expect(getVideoStream).toHaveBeenCalledWith({
145
- deviceId: undefined,
146
- width: 1280,
147
- height: 720,
148
- facingMode: 'user',
149
- });
151
+ expect(getVideoStream).toHaveBeenCalledWith(
152
+ {
153
+ deviceId: undefined,
154
+ width: 1280,
155
+ height: 720,
156
+ facingMode: 'user',
157
+ },
158
+ expect.any(Tracer),
159
+ );
150
160
 
151
161
  await manager.selectDirection('back');
152
162
 
153
- expect(getVideoStream).toHaveBeenCalledWith({
154
- deviceId: undefined,
155
- facingMode: 'environment',
156
- width: 1280,
157
- height: 720,
158
- });
163
+ expect(getVideoStream).toHaveBeenCalledWith(
164
+ {
165
+ deviceId: undefined,
166
+ facingMode: 'environment',
167
+ width: 1280,
168
+ height: 720,
169
+ },
170
+ expect.any(Tracer),
171
+ );
159
172
  });
160
173
 
161
174
  it(`shouldn't set deviceId and facingMode at the same time`, async () => {
@@ -163,11 +176,14 @@ describe('CameraManager', () => {
163
176
 
164
177
  await manager.flip();
165
178
 
166
- expect(getVideoStream).toHaveBeenCalledWith({
167
- facingMode: 'environment',
168
- width: 1280,
169
- height: 720,
170
- });
179
+ expect(getVideoStream).toHaveBeenCalledWith(
180
+ {
181
+ facingMode: 'environment',
182
+ width: 1280,
183
+ height: 720,
184
+ },
185
+ expect.any(Tracer),
186
+ );
171
187
 
172
188
  const deviceId = mockVideoDevices[1].deviceId;
173
189
  await manager.select(deviceId);
@@ -24,6 +24,7 @@ import {
24
24
  SoundStateChangeHandler,
25
25
  } from '../../helpers/sound-detector';
26
26
  import { PermissionsContext } from '../../permissions';
27
+ import { Tracer } from '../../stats';
27
28
 
28
29
  vi.mock('../devices.ts', () => {
29
30
  console.log('MOCKING devices API');
@@ -92,9 +93,10 @@ describe('MicrophoneManager', () => {
92
93
  it('get stream', async () => {
93
94
  await manager.enable();
94
95
 
95
- expect(getAudioStream).toHaveBeenCalledWith({
96
- deviceId: undefined,
97
- });
96
+ expect(getAudioStream).toHaveBeenCalledWith(
97
+ { deviceId: undefined },
98
+ expect.any(Tracer),
99
+ );
98
100
  });
99
101
 
100
102
  it('should get device id from stream', async () => {
@@ -7,6 +7,7 @@ import * as RxUtils from '../../store/rxUtils';
7
7
  import { mockCall, mockDeviceIds$, mockScreenShareStream } from './mocks';
8
8
  import { getScreenShareStream } from '../devices';
9
9
  import { TrackType } from '../../gen/video/sfu/models/models';
10
+ import { Tracer } from '../../stats';
10
11
 
11
12
  vi.mock('../devices.ts', () => {
12
13
  console.log('MOCKING devices API');
@@ -50,7 +51,7 @@ describe('ScreenShareManager', () => {
50
51
  });
51
52
 
52
53
  it('select device', async () => {
53
- await expect(manager.select('any-device-id')).rejects.toThrowError();
54
+ await expect(manager.select()).rejects.toThrowError();
54
55
  });
55
56
 
56
57
  it('get stream', async () => {
@@ -62,6 +63,7 @@ describe('ScreenShareManager', () => {
62
63
  expect.objectContaining({
63
64
  deviceId: undefined,
64
65
  }),
66
+ expect.any(Tracer),
65
67
  );
66
68
  });
67
69
 
@@ -75,6 +77,7 @@ describe('ScreenShareManager', () => {
75
77
  deviceId: undefined,
76
78
  audio: false,
77
79
  }),
80
+ expect.any(Tracer),
78
81
  );
79
82
  });
80
83
 
@@ -106,6 +109,7 @@ describe('ScreenShareManager', () => {
106
109
  height: 600,
107
110
  },
108
111
  }),
112
+ expect.any(Tracer),
109
113
  );
110
114
  });
111
115
 
@@ -7,6 +7,7 @@ import {
7
7
  import { Call } from '../../Call';
8
8
  import { of, Subject } from 'rxjs';
9
9
  import { BrowserPermission } from '../BrowserPermission';
10
+ import { Tracer } from '../../stats';
10
11
 
11
12
  export const mockVideoDevices = [
12
13
  {
@@ -93,13 +94,11 @@ export const mockCall = (): Partial<Call> => {
93
94
  });
94
95
  return {
95
96
  state: callState,
96
- publishVideoStream: vi.fn(),
97
- publishAudioStream: vi.fn(),
98
- publishScreenShareStream: vi.fn(),
99
97
  publish: vi.fn(),
100
98
  stopPublish: vi.fn(),
101
99
  notifyNoiseCancellationStarting: vi.fn().mockResolvedValue(undefined),
102
100
  notifyNoiseCancellationStopped: vi.fn().mockResolvedValue(undefined),
101
+ tracer: new Tracer('tests'),
103
102
  };
104
103
  };
105
104