@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.
- package/CHANGELOG.md +14 -0
- package/dist/index.browser.es.js +20 -9
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +20 -9
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +20 -9
- package/dist/index.es.js.map +1 -1
- package/dist/src/gen/coordinator/index.d.ts +12 -0
- package/dist/src/rtc/videoLayers.d.ts +1 -1
- package/package.json +1 -1
- package/src/StreamSfuClient.ts +5 -7
- package/src/devices/ScreenShareManager.ts +16 -0
- package/src/devices/__tests__/ScreenShareManager.test.ts +35 -7
- package/src/devices/__tests__/mocks.ts +4 -0
- package/src/events/__tests__/call.test.ts +4 -0
- package/src/gen/coordinator/index.ts +12 -0
- package/src/rtc/Publisher.ts +8 -1
- package/src/rtc/__tests__/videoLayers.test.ts +11 -0
- package/src/rtc/videoLayers.ts +2 -1
|
@@ -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
package/src/StreamSfuClient.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
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);
|
|
@@ -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
|
*
|
package/src/rtc/Publisher.ts
CHANGED
|
@@ -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(
|
|
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;
|
package/src/rtc/videoLayers.ts
CHANGED
|
@@ -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 ??
|
|
144
|
+
maxBitrate: preferences?.maxBitrate ?? defaultMaxBitrate,
|
|
144
145
|
maxFramerate: preferences?.maxFramerate ?? 30,
|
|
145
146
|
},
|
|
146
147
|
];
|