@stream-io/video-client 0.7.11 → 0.7.13

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.
@@ -4113,6 +4113,12 @@ export interface ScreensharingSettings {
4113
4113
  * @memberof ScreensharingSettings
4114
4114
  */
4115
4115
  enabled: boolean;
4116
+ /**
4117
+ *
4118
+ * @type {TargetResolution}
4119
+ * @memberof ScreensharingSettings
4120
+ */
4121
+ target_resolution?: TargetResolution;
4116
4122
  }
4117
4123
  /**
4118
4124
  *
@@ -4132,6 +4138,12 @@ export interface ScreensharingSettingsRequest {
4132
4138
  * @memberof ScreensharingSettingsRequest
4133
4139
  */
4134
4140
  enabled?: boolean;
4141
+ /**
4142
+ *
4143
+ * @type {TargetResolution}
4144
+ * @memberof ScreensharingSettingsRequest
4145
+ */
4146
+ target_resolution?: TargetResolution;
4135
4147
  }
4136
4148
  /**
4137
4149
  *
@@ -24,4 +24,4 @@ export declare const findOptimalVideoLayers: (videoTrack: MediaStreamTrack, targ
24
24
  * @param currentHeight the current height of the track.
25
25
  */
26
26
  export declare const getComputedMaxBitrate: (targetResolution: TargetResolution, currentWidth: number, currentHeight: number) => number;
27
- export declare const findOptimalScreenSharingLayers: (videoTrack: MediaStreamTrack, preferences?: ScreenShareSettings) => OptimalVideoLayer[];
27
+ export declare const findOptimalScreenSharingLayers: (videoTrack: MediaStreamTrack, preferences?: ScreenShareSettings, defaultMaxBitrate?: number) => OptimalVideoLayer[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-client",
3
- "version": "0.7.11",
3
+ "version": "0.7.13",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
@@ -34,7 +34,7 @@ import {
34
34
  sleep,
35
35
  } from './coordinator/connection/utils';
36
36
  import { SFUResponse } from './gen/coordinator';
37
- import { Logger } from './coordinator/connection/types';
37
+ import { LogLevel, Logger } from './coordinator/connection/types';
38
38
  import { getLogger } from './logger';
39
39
 
40
40
  export type StreamSfuClientConstructor = {
@@ -228,6 +228,7 @@ export class StreamSfuClient {
228
228
  tracks: subscriptions,
229
229
  }),
230
230
  this.logger,
231
+ 'debug',
231
232
  );
232
233
  };
233
234
 
@@ -307,6 +308,7 @@ export class StreamSfuClient {
307
308
  sessionId: this.sessionId,
308
309
  }),
309
310
  this.logger,
311
+ 'debug',
310
312
  );
311
313
  };
312
314
 
@@ -421,6 +423,7 @@ const MAX_RETRIES = 5;
421
423
  const retryable = async <I extends object, O extends SfuResponseWithError>(
422
424
  rpc: () => UnaryCall<I, O>,
423
425
  logger: Logger,
426
+ level: LogLevel = 'error',
424
427
  ) => {
425
428
  let retryAttempt = 0;
426
429
  let rpcCallResult: FinishedUnaryCall<I, O>;
@@ -431,16 +434,11 @@ const retryable = async <I extends object, O extends SfuResponseWithError>(
431
434
  }
432
435
 
433
436
  rpcCallResult = await rpc();
434
- logger(
435
- 'trace',
436
- `SFU RPC response received for ${rpcCallResult.method.name}`,
437
- rpcCallResult,
438
- );
439
437
 
440
438
  // if the RPC call failed, log the error and retry
441
439
  if (rpcCallResult.response.error) {
442
440
  logger(
443
- 'error',
441
+ level,
444
442
  `SFU RPC Error (${rpcCallResult.method.name}):`,
445
443
  rpcCallResult.response.error,
446
444
  );
@@ -5,6 +5,7 @@ import { Call } from '../Call';
5
5
  import { TrackType } from '../gen/video/sfu/models/models';
6
6
  import { getScreenShareStream } from './devices';
7
7
  import { ScreenShareSettings } from '../types';
8
+ import { createSubscription } from '../store/rxUtils';
8
9
 
9
10
  export class ScreenShareManager extends InputMediaDeviceManager<
10
11
  ScreenShareState,
@@ -12,6 +13,21 @@ export class ScreenShareManager extends InputMediaDeviceManager<
12
13
  > {
13
14
  constructor(call: Call) {
14
15
  super(call, new ScreenShareState(), TrackType.SCREEN_SHARE);
16
+
17
+ this.subscriptions.push(
18
+ createSubscription(call.state.settings$, (settings) => {
19
+ const maybeTargetResolution = settings?.screensharing.target_resolution;
20
+
21
+ if (maybeTargetResolution) {
22
+ this.setDefaultConstraints({
23
+ video: {
24
+ width: maybeTargetResolution.width,
25
+ height: maybeTargetResolution.height,
26
+ },
27
+ });
28
+ }
29
+ }),
30
+ );
15
31
  }
16
32
 
17
33
  /**
@@ -58,9 +58,11 @@ describe('ScreenShareManager', () => {
58
58
  await manager.enable();
59
59
  expect(manager.state.status).toEqual('enabled');
60
60
 
61
- expect(getScreenShareStream).toHaveBeenCalledWith({
62
- deviceId: undefined,
63
- });
61
+ expect(getScreenShareStream).toHaveBeenCalledWith(
62
+ expect.objectContaining({
63
+ deviceId: undefined,
64
+ }),
65
+ );
64
66
  });
65
67
 
66
68
  it('get stream with no audio', async () => {
@@ -68,10 +70,12 @@ describe('ScreenShareManager', () => {
68
70
  await manager.enable();
69
71
  expect(manager.state.status).toEqual('enabled');
70
72
 
71
- expect(getScreenShareStream).toHaveBeenCalledWith({
72
- deviceId: undefined,
73
- audio: false,
74
- });
73
+ expect(getScreenShareStream).toHaveBeenCalledWith(
74
+ expect.objectContaining({
75
+ deviceId: undefined,
76
+ audio: false,
77
+ }),
78
+ );
75
79
  });
76
80
 
77
81
  it('should get device id from stream', async () => {
@@ -81,6 +85,30 @@ describe('ScreenShareManager', () => {
81
85
  expect(manager.state.selectedDevice).toEqual('screen');
82
86
  });
83
87
 
88
+ it('should use call settings to set up constraints', async () => {
89
+ const call = manager['call'];
90
+ call.state.setCurrentValue(call.state['settingsSubject'], {
91
+ // @ts-expect-error partial data
92
+ screensharing: {
93
+ target_resolution: {
94
+ width: 800,
95
+ height: 600,
96
+ bitrate: 192000,
97
+ },
98
+ },
99
+ });
100
+
101
+ await manager.enable();
102
+ expect(getScreenShareStream).toHaveBeenCalledWith(
103
+ expect.objectContaining({
104
+ video: {
105
+ width: 800,
106
+ height: 600,
107
+ },
108
+ }),
109
+ );
110
+ });
111
+
84
112
  it('publishes screen share stream', async () => {
85
113
  const call = manager['call'];
86
114
  call.state.setCallingState(CallingState.JOINED);
@@ -84,6 +84,10 @@ export const mockCall = (): Partial<Call> => {
84
84
  mode: NoiseCancellationSettingsModeEnum.AVAILABLE,
85
85
  },
86
86
  },
87
+ // @ts-expect-error partial data
88
+ screensharing: {
89
+ target_resolution: undefined,
90
+ },
87
91
  },
88
92
  });
89
93
  return {
@@ -305,6 +305,10 @@ const fakeMetadata = (): CallResponse => {
305
305
  auto_cancel_timeout_ms: 30000,
306
306
  incoming_call_timeout_ms: 30000,
307
307
  },
308
+ // @ts-ignore
309
+ screensharing: {
310
+ target_resolution: undefined,
311
+ },
308
312
  },
309
313
  };
310
314
  };
@@ -4104,6 +4104,12 @@ export interface ScreensharingSettings {
4104
4104
  * @memberof ScreensharingSettings
4105
4105
  */
4106
4106
  enabled: boolean;
4107
+ /**
4108
+ *
4109
+ * @type {TargetResolution}
4110
+ * @memberof ScreensharingSettings
4111
+ */
4112
+ target_resolution?: TargetResolution;
4107
4113
  }
4108
4114
  /**
4109
4115
  *
@@ -4123,6 +4129,12 @@ export interface ScreensharingSettingsRequest {
4123
4129
  * @memberof ScreensharingSettingsRequest
4124
4130
  */
4125
4131
  enabled?: boolean;
4132
+ /**
4133
+ *
4134
+ * @type {TargetResolution}
4135
+ * @memberof ScreensharingSettingsRequest
4136
+ */
4137
+ target_resolution?: TargetResolution;
4126
4138
  }
4127
4139
  /**
4128
4140
  *
@@ -240,11 +240,18 @@ export class Publisher {
240
240
  if (!transceiver) {
241
241
  const { settings } = this.state;
242
242
  const targetResolution = settings?.video.target_resolution;
243
+ const screenShareBitrate =
244
+ settings?.screensharing.target_resolution?.bitrate;
245
+
243
246
  const videoEncodings =
244
247
  trackType === TrackType.VIDEO
245
248
  ? findOptimalVideoLayers(track, targetResolution)
246
249
  : trackType === TrackType.SCREEN_SHARE
247
- ? findOptimalScreenSharingLayers(track, opts.screenShareSettings)
250
+ ? findOptimalScreenSharingLayers(
251
+ track,
252
+ opts.screenShareSettings,
253
+ screenShareBitrate,
254
+ )
248
255
  : undefined;
249
256
 
250
257
  let preferredCodec = opts.preferredCodec;
@@ -28,6 +28,17 @@ describe('videoLayers', () => {
28
28
  ]);
29
29
  });
30
30
 
31
+ it('should use default max bitrate if none is provided in preferences', () => {
32
+ const track = new MediaStreamTrack();
33
+ vi.spyOn(track, 'getSettings').mockReturnValue({
34
+ width: 1920,
35
+ height: 1080,
36
+ });
37
+
38
+ const layers = findOptimalScreenSharingLayers(track, undefined, 192000);
39
+ expect(layers).toMatchObject([{ maxBitrate: 192000 }]);
40
+ });
41
+
31
42
  it('should find optimal video layers', () => {
32
43
  const track = new MediaStreamTrack();
33
44
  const width = 1920;
@@ -131,6 +131,7 @@ const withSimulcastConstraints = (
131
131
  export const findOptimalScreenSharingLayers = (
132
132
  videoTrack: MediaStreamTrack,
133
133
  preferences?: ScreenShareSettings,
134
+ defaultMaxBitrate = 3000000,
134
135
  ): OptimalVideoLayer[] => {
135
136
  const settings = videoTrack.getSettings();
136
137
  return [
@@ -140,7 +141,7 @@ export const findOptimalScreenSharingLayers = (
140
141
  width: settings.width || 0,
141
142
  height: settings.height || 0,
142
143
  scaleResolutionDownBy: 1,
143
- maxBitrate: preferences?.maxBitrate ?? 3000000,
144
+ maxBitrate: preferences?.maxBitrate ?? defaultMaxBitrate,
144
145
  maxFramerate: preferences?.maxFramerate ?? 30,
145
146
  },
146
147
  ];