@stream-io/video-client 0.3.8 → 0.3.10
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 +481 -152
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +480 -150
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +481 -152
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +50 -10
- package/dist/src/helpers/DynascaleManager.d.ts +70 -0
- package/dist/src/helpers/__tests__/DynascaleManager.test.d.ts +4 -0
- package/dist/src/types.d.ts +4 -5
- package/dist/version.d.ts +1 -1
- package/index.ts +2 -1
- package/package.json +8 -7
- package/src/Call.ts +140 -24
- package/src/events/__tests__/participant.test.ts +4 -1
- package/src/events/participant.ts +4 -1
- package/src/helpers/DynascaleManager.ts +345 -0
- package/src/helpers/__tests__/DynascaleManager.test.ts +391 -0
- package/src/sorting/__tests__/participant-data.ts +24 -6
- package/src/sorting/presets.ts +2 -2
- package/src/store/__tests__/CallState.test.ts +3 -3
- package/src/store/rxUtils.ts +10 -1
- package/src/types.ts +5 -5
package/dist/src/Call.d.ts
CHANGED
|
@@ -2,21 +2,16 @@ import { SfuEventKinds, SfuEventListener } from './rtc';
|
|
|
2
2
|
import { TrackType } from './gen/video/sfu/models/models';
|
|
3
3
|
import { CallState } from './store';
|
|
4
4
|
import { AcceptCallResponse, BlockUserResponse, EndCallResponse, GetCallResponse, GetOrCreateCallRequest, GetOrCreateCallResponse, GoLiveRequest, GoLiveResponse, ListRecordingsResponse, MuteUsersResponse, PinRequest, PinResponse, QueryMembersRequest, QueryMembersResponse, RejectCallResponse, RequestPermissionRequest, RequestPermissionResponse, SendEventResponse, SendReactionRequest, SendReactionResponse, StartBroadcastingResponse, StartRecordingResponse, StopBroadcastingResponse, StopLiveResponse, StopRecordingResponse, UnblockUserResponse, UnpinRequest, UnpinResponse, UpdateCallMembersRequest, UpdateCallMembersResponse, UpdateCallRequest, UpdateCallResponse, UpdateUserPermissionsRequest, UpdateUserPermissionsResponse } from './gen/coordinator';
|
|
5
|
-
import { CallConstructor, CallLeaveOptions, DebounceType, JoinCallData, PublishOptions, SubscriptionChanges } from './types';
|
|
6
|
-
import {
|
|
5
|
+
import { CallConstructor, CallLeaveOptions, DebounceType, JoinCallData, PublishOptions, SubscriptionChanges, VideoTrackType } from './types';
|
|
6
|
+
import { DynascaleManager } from './helpers/DynascaleManager';
|
|
7
7
|
import { PermissionsContext } from './permissions';
|
|
8
8
|
import { StreamClient } from './coordinator/connection/client';
|
|
9
9
|
import { CallEventHandler, CallEventTypes, EventTypes, Logger } from './coordinator/connection/types';
|
|
10
|
-
import { CameraManager } from './devices
|
|
11
|
-
import { MicrophoneManager } from './devices/MicrophoneManager';
|
|
10
|
+
import { CameraManager, MicrophoneManager } from './devices';
|
|
12
11
|
/**
|
|
13
12
|
* An object representation of a `Call`.
|
|
14
13
|
*/
|
|
15
14
|
export declare class Call {
|
|
16
|
-
/**
|
|
17
|
-
* ViewportTracker instance
|
|
18
|
-
*/
|
|
19
|
-
readonly viewportTracker: ViewportTracker;
|
|
20
15
|
/**
|
|
21
16
|
* The type of the call.
|
|
22
17
|
*/
|
|
@@ -46,6 +41,10 @@ export declare class Call {
|
|
|
46
41
|
* Device manager for the microhpone
|
|
47
42
|
*/
|
|
48
43
|
readonly microphone: MicrophoneManager;
|
|
44
|
+
/**
|
|
45
|
+
* The DynascaleManager instance.
|
|
46
|
+
*/
|
|
47
|
+
readonly dynascaleManager: DynascaleManager;
|
|
49
48
|
/**
|
|
50
49
|
* Flag telling whether this call is a "ringing" call.
|
|
51
50
|
*/
|
|
@@ -225,11 +224,11 @@ export declare class Call {
|
|
|
225
224
|
* You have to create a subscription for each participant for all the different kinds of tracks you want to receive.
|
|
226
225
|
* You can only subscribe for tracks after the participant started publishing the given kind of track.
|
|
227
226
|
*
|
|
228
|
-
* @param
|
|
227
|
+
* @param trackType the kind of subscription to update.
|
|
229
228
|
* @param changes the list of subscription changes to do.
|
|
230
229
|
* @param type the debounce type to use for the update.
|
|
231
230
|
*/
|
|
232
|
-
updateSubscriptionsPartial: (
|
|
231
|
+
updateSubscriptionsPartial: (trackType: VideoTrackType | 'video' | 'screen', changes: SubscriptionChanges, type?: DebounceType) => void;
|
|
233
232
|
private updateSubscriptions;
|
|
234
233
|
/**
|
|
235
234
|
* Will enhance the reported stats with additional participant-specific information (`callStatsReport$` state [store variable](./StreamVideoClient.md/#readonlystatestore)).
|
|
@@ -477,4 +476,45 @@ export declare class Call {
|
|
|
477
476
|
}) => Promise<SendEventResponse>;
|
|
478
477
|
private initCamera;
|
|
479
478
|
private initMic;
|
|
479
|
+
/**
|
|
480
|
+
* Will begin tracking the given element for visibility changes within the
|
|
481
|
+
* configured viewport element (`call.setViewport`).
|
|
482
|
+
*
|
|
483
|
+
* @param element the element to track.
|
|
484
|
+
* @param sessionId the session id.
|
|
485
|
+
* @param trackType the video mode.
|
|
486
|
+
*/
|
|
487
|
+
trackElementVisibility: <T extends HTMLElement>(element: T, sessionId: string, trackType: VideoTrackType) => () => void;
|
|
488
|
+
/**
|
|
489
|
+
* Sets the viewport element to track bound video elements for visibility.
|
|
490
|
+
*
|
|
491
|
+
* @param element the viewport element.
|
|
492
|
+
*/
|
|
493
|
+
setViewport: <T extends HTMLElement>(element: T) => () => void;
|
|
494
|
+
/**
|
|
495
|
+
* Binds a DOM <video> element to the given session id.
|
|
496
|
+
* This method will make sure that the video element will play
|
|
497
|
+
* the correct video stream for the given session id.
|
|
498
|
+
*
|
|
499
|
+
* Under the hood, it would also keep track of the video element dimensions
|
|
500
|
+
* and update the subscription accordingly in order to optimize the bandwidth.
|
|
501
|
+
*
|
|
502
|
+
* If a "viewport" is configured, the video element will be automatically
|
|
503
|
+
* tracked for visibility and the subscription will be updated accordingly.
|
|
504
|
+
*
|
|
505
|
+
* @param videoElement the video element to bind to.
|
|
506
|
+
* @param sessionId the session id.
|
|
507
|
+
* @param trackType the kind of video.
|
|
508
|
+
*/
|
|
509
|
+
bindVideoElement: (videoElement: HTMLVideoElement, sessionId: string, trackType: VideoTrackType) => (() => void) | undefined;
|
|
510
|
+
/**
|
|
511
|
+
* Binds a DOM <audio> element to the given session id.
|
|
512
|
+
*
|
|
513
|
+
* This method will make sure that the audio element will
|
|
514
|
+
* play the correct audio stream for the given session id.
|
|
515
|
+
*
|
|
516
|
+
* @param audioElement the audio element to bind to.
|
|
517
|
+
* @param sessionId the session id.
|
|
518
|
+
*/
|
|
519
|
+
bindAudioElement: (audioElement: HTMLAudioElement, sessionId: string) => (() => void) | undefined;
|
|
480
520
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Call } from '../Call';
|
|
2
|
+
import { VideoTrackType } from '../types';
|
|
3
|
+
import { ViewportTracker } from './ViewportTracker';
|
|
4
|
+
/**
|
|
5
|
+
* A manager class that handles dynascale related tasks like:
|
|
6
|
+
*
|
|
7
|
+
* - binding video elements to session ids
|
|
8
|
+
* - binding audio elements to session ids
|
|
9
|
+
* - tracking element visibility
|
|
10
|
+
* - updating subscriptions based on viewport visibility
|
|
11
|
+
* - updating subscriptions based on video element dimensions
|
|
12
|
+
* - updating subscriptions based on published tracks
|
|
13
|
+
*/
|
|
14
|
+
export declare class DynascaleManager {
|
|
15
|
+
/**
|
|
16
|
+
* The viewport tracker instance.
|
|
17
|
+
*/
|
|
18
|
+
readonly viewportTracker: ViewportTracker;
|
|
19
|
+
private logger;
|
|
20
|
+
private call;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new DynascaleManager instance.
|
|
23
|
+
*
|
|
24
|
+
* @param call the call to manage.
|
|
25
|
+
*/
|
|
26
|
+
constructor(call: Call);
|
|
27
|
+
/**
|
|
28
|
+
* Will begin tracking the given element for visibility changes within the
|
|
29
|
+
* configured viewport element (`call.setViewport`).
|
|
30
|
+
*
|
|
31
|
+
* @param element the element to track.
|
|
32
|
+
* @param sessionId the session id.
|
|
33
|
+
* @param trackType the kind of video.
|
|
34
|
+
* @returns Untrack.
|
|
35
|
+
*/
|
|
36
|
+
trackElementVisibility: <T extends HTMLElement>(element: T, sessionId: string, trackType: VideoTrackType) => () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Sets the viewport element to track bound video elements for visibility.
|
|
39
|
+
*
|
|
40
|
+
* @param element the viewport element.
|
|
41
|
+
*/
|
|
42
|
+
setViewport: <T extends HTMLElement>(element: T) => () => void;
|
|
43
|
+
/**
|
|
44
|
+
* Binds a DOM <video> element to the given session id.
|
|
45
|
+
* This method will make sure that the video element will play
|
|
46
|
+
* the correct video stream for the given session id.
|
|
47
|
+
*
|
|
48
|
+
* Under the hood, it would also keep track of the video element dimensions
|
|
49
|
+
* and update the subscription accordingly in order to optimize the bandwidth.
|
|
50
|
+
*
|
|
51
|
+
* If a "viewport" is configured, the video element will be automatically
|
|
52
|
+
* tracked for visibility and the subscription will be updated accordingly.
|
|
53
|
+
*
|
|
54
|
+
* @param videoElement the video element to bind to.
|
|
55
|
+
* @param sessionId the session id.
|
|
56
|
+
* @param trackType the kind of video.
|
|
57
|
+
*/
|
|
58
|
+
bindVideoElement: (videoElement: HTMLVideoElement, sessionId: string, trackType: VideoTrackType) => (() => void) | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Binds a DOM <audio> element to the given session id.
|
|
61
|
+
*
|
|
62
|
+
* This method will make sure that the audio element will
|
|
63
|
+
* play the correct audio stream for the given session id.
|
|
64
|
+
*
|
|
65
|
+
* @param audioElement the audio element to bind to.
|
|
66
|
+
* @param sessionId the session id.
|
|
67
|
+
* @returns a cleanup function that will unbind the audio element.
|
|
68
|
+
*/
|
|
69
|
+
bindAudioElement: (audioElement: HTMLAudioElement, sessionId: string) => (() => void) | undefined;
|
|
70
|
+
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare enum VisibilityState {
|
|
|
11
11
|
INVISIBLE = "INVISIBLE"
|
|
12
12
|
}
|
|
13
13
|
export declare enum DebounceType {
|
|
14
|
-
IMMEDIATE =
|
|
14
|
+
IMMEDIATE = 20,
|
|
15
15
|
FAST = 100,
|
|
16
16
|
MEDIUM = 600,
|
|
17
17
|
SLOW = 1200
|
|
@@ -56,11 +56,9 @@ export interface StreamVideoParticipant extends Participant {
|
|
|
56
56
|
*/
|
|
57
57
|
reaction?: StreamReaction;
|
|
58
58
|
/**
|
|
59
|
-
* The visibility state of the participant's
|
|
60
|
-
* within the pre-configured viewport.
|
|
61
|
-
* @default VisibilityState.UNKNOWN
|
|
59
|
+
* The visibility state of the participant's tracks within a defined viewport.
|
|
62
60
|
*/
|
|
63
|
-
viewportVisibilityState?: VisibilityState
|
|
61
|
+
viewportVisibilityState?: Record<VideoTrackType, VisibilityState>;
|
|
64
62
|
}
|
|
65
63
|
export interface StreamVideoLocalParticipant extends StreamVideoParticipant {
|
|
66
64
|
/**
|
|
@@ -82,6 +80,7 @@ export interface StreamVideoLocalParticipant extends StreamVideoParticipant {
|
|
|
82
80
|
*/
|
|
83
81
|
audioOutputDeviceId?: string;
|
|
84
82
|
}
|
|
83
|
+
export type VideoTrackType = 'videoTrack' | 'screenShareTrack';
|
|
85
84
|
/**
|
|
86
85
|
* Represents a participant's pin state.
|
|
87
86
|
*/
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.3.
|
|
1
|
+
export declare const version = "0.3.10";
|
package/index.ts
CHANGED
|
@@ -18,9 +18,10 @@ export * from './src/StreamSfuClient';
|
|
|
18
18
|
export * from './src/devices';
|
|
19
19
|
export * from './src/store';
|
|
20
20
|
export * from './src/sorting';
|
|
21
|
+
export * from './src/helpers/DynascaleManager';
|
|
21
22
|
export * from './src/helpers/ViewportTracker';
|
|
22
|
-
|
|
23
23
|
export * from './src/helpers/sound-detector';
|
|
24
24
|
export * as Browsers from './src/helpers/browsers';
|
|
25
|
+
|
|
25
26
|
export * from './src/client-details';
|
|
26
27
|
export * from './src/logger';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-client",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"packageManager": "yarn@3.2.4",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
@@ -46,20 +46,21 @@
|
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@openapitools/openapi-generator-cli": "^2.6.0",
|
|
48
48
|
"@rollup/plugin-replace": "^5.0.2",
|
|
49
|
-
"@rollup/plugin-typescript": "^11.1.
|
|
49
|
+
"@rollup/plugin-typescript": "^11.1.2",
|
|
50
50
|
"@types/jsonwebtoken": "^9.0.1",
|
|
51
51
|
"@types/rimraf": "^3.0.2",
|
|
52
52
|
"@types/sdp-transform": "^2.4.6",
|
|
53
53
|
"@types/ua-parser-js": "^0.7.36",
|
|
54
54
|
"@types/ws": "^8.5.4",
|
|
55
|
-
"@vitest/coverage-
|
|
55
|
+
"@vitest/coverage-v8": "^0.34.2",
|
|
56
56
|
"dotenv": "^16.3.1",
|
|
57
|
+
"happy-dom": "^10.11.0",
|
|
57
58
|
"prettier": "^2.8.4",
|
|
58
59
|
"rimraf": "^3.0.2",
|
|
59
|
-
"rollup": "^3.
|
|
60
|
+
"rollup": "^3.28.1",
|
|
60
61
|
"typescript": "^4.9.5",
|
|
61
|
-
"vite": "^4.
|
|
62
|
-
"vitest": "^0.
|
|
63
|
-
"vitest-mock-extended": "^1.
|
|
62
|
+
"vite": "^4.4.9",
|
|
63
|
+
"vitest": "^0.34.3",
|
|
64
|
+
"vitest-mock-extended": "^1.2.0"
|
|
64
65
|
}
|
|
65
66
|
}
|
package/src/Call.ts
CHANGED
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
StreamVideoParticipant,
|
|
77
77
|
StreamVideoParticipantPatches,
|
|
78
78
|
SubscriptionChanges,
|
|
79
|
+
VideoTrackType,
|
|
79
80
|
VisibilityState,
|
|
80
81
|
} from './types';
|
|
81
82
|
import {
|
|
@@ -96,7 +97,7 @@ import {
|
|
|
96
97
|
createStatsReporter,
|
|
97
98
|
StatsReporter,
|
|
98
99
|
} from './stats/state-store-stats-reporter';
|
|
99
|
-
import {
|
|
100
|
+
import { DynascaleManager } from './helpers/DynascaleManager';
|
|
100
101
|
import { PermissionsContext } from './permissions';
|
|
101
102
|
import { CallTypes } from './CallType';
|
|
102
103
|
import { StreamClient } from './coordinator/connection/client';
|
|
@@ -115,19 +116,12 @@ import {
|
|
|
115
116
|
} from './coordinator/connection/types';
|
|
116
117
|
import { getClientDetails, getSdkInfo } from './client-details';
|
|
117
118
|
import { getLogger } from './logger';
|
|
118
|
-
import { CameraManager } from './devices
|
|
119
|
-
import { MicrophoneManager } from './devices/MicrophoneManager';
|
|
120
|
-
import { CameraDirection } from './devices/CameraManagerState';
|
|
119
|
+
import { CameraDirection, CameraManager, MicrophoneManager } from './devices';
|
|
121
120
|
|
|
122
121
|
/**
|
|
123
122
|
* An object representation of a `Call`.
|
|
124
123
|
*/
|
|
125
124
|
export class Call {
|
|
126
|
-
/**
|
|
127
|
-
* ViewportTracker instance
|
|
128
|
-
*/
|
|
129
|
-
readonly viewportTracker = new ViewportTracker();
|
|
130
|
-
|
|
131
125
|
/**
|
|
132
126
|
* The type of the call.
|
|
133
127
|
*/
|
|
@@ -164,6 +158,11 @@ export class Call {
|
|
|
164
158
|
*/
|
|
165
159
|
readonly microphone: MicrophoneManager;
|
|
166
160
|
|
|
161
|
+
/**
|
|
162
|
+
* The DynascaleManager instance.
|
|
163
|
+
*/
|
|
164
|
+
readonly dynascaleManager = new DynascaleManager(this);
|
|
165
|
+
|
|
167
166
|
/**
|
|
168
167
|
* Flag telling whether this call is a "ringing" call.
|
|
169
168
|
*/
|
|
@@ -202,7 +201,7 @@ export class Call {
|
|
|
202
201
|
* A typical use case is to clean up some global event handlers.
|
|
203
202
|
* @private
|
|
204
203
|
*/
|
|
205
|
-
private readonly leaveCallHooks: Function
|
|
204
|
+
private readonly leaveCallHooks: Set<Function> = new Set();
|
|
206
205
|
|
|
207
206
|
private readonly streamClientBasePath: string;
|
|
208
207
|
private streamClientEventHandlers = new Map<Function, CallEventHandler>();
|
|
@@ -253,12 +252,12 @@ export class Call {
|
|
|
253
252
|
this.state.updateFromEvent(event);
|
|
254
253
|
});
|
|
255
254
|
|
|
256
|
-
this.leaveCallHooks.
|
|
255
|
+
this.leaveCallHooks.add(
|
|
257
256
|
registerEventHandlers(this, this.state, this.dispatcher),
|
|
258
257
|
);
|
|
259
258
|
this.registerEffects();
|
|
260
259
|
|
|
261
|
-
this.leaveCallHooks.
|
|
260
|
+
this.leaveCallHooks.add(
|
|
262
261
|
createSubscription(
|
|
263
262
|
this.trackSubscriptionsSubject.pipe(
|
|
264
263
|
debounce((v) => timer(v.type)),
|
|
@@ -273,13 +272,15 @@ export class Call {
|
|
|
273
272
|
}
|
|
274
273
|
|
|
275
274
|
private registerEffects() {
|
|
276
|
-
this.leaveCallHooks.
|
|
275
|
+
this.leaveCallHooks.add(
|
|
277
276
|
// handles updating the permissions context when the settings change.
|
|
278
277
|
createSubscription(this.state.settings$, (settings) => {
|
|
279
278
|
if (!settings) return;
|
|
280
279
|
this.permissionsContext.setCallSettings(settings);
|
|
281
280
|
}),
|
|
281
|
+
);
|
|
282
282
|
|
|
283
|
+
this.leaveCallHooks.add(
|
|
283
284
|
// handle the case when the user permissions are modified.
|
|
284
285
|
createSubscription(this.state.ownCapabilities$, (ownCapabilities) => {
|
|
285
286
|
// update the permission context.
|
|
@@ -306,7 +307,9 @@ export class Call {
|
|
|
306
307
|
}
|
|
307
308
|
}
|
|
308
309
|
}),
|
|
310
|
+
);
|
|
309
311
|
|
|
312
|
+
this.leaveCallHooks.add(
|
|
310
313
|
// handles the case when the user is blocked by the call owner.
|
|
311
314
|
createSubscription(this.state.blockedUserIds$, async (blockedUserIds) => {
|
|
312
315
|
if (!blockedUserIds) return;
|
|
@@ -316,7 +319,9 @@ export class Call {
|
|
|
316
319
|
await this.leave();
|
|
317
320
|
}
|
|
318
321
|
}),
|
|
322
|
+
);
|
|
319
323
|
|
|
324
|
+
this.leaveCallHooks.add(
|
|
320
325
|
// watch for auto drop cancellation
|
|
321
326
|
createSubscription(this.state.callingState$, (callingState) => {
|
|
322
327
|
if (!this.ringing) return;
|
|
@@ -329,7 +334,9 @@ export class Call {
|
|
|
329
334
|
this.dropTimeout = undefined;
|
|
330
335
|
}
|
|
331
336
|
}),
|
|
337
|
+
);
|
|
332
338
|
|
|
339
|
+
this.leaveCallHooks.add(
|
|
333
340
|
// "ringing" mode effects and event handlers
|
|
334
341
|
createSubscription(this.ringingSubject, (isRinging) => {
|
|
335
342
|
if (!isRinging) return;
|
|
@@ -337,7 +344,7 @@ export class Call {
|
|
|
337
344
|
if (this.state.callingState === CallingState.IDLE) {
|
|
338
345
|
this.state.setCallingState(CallingState.RINGING);
|
|
339
346
|
}
|
|
340
|
-
this.leaveCallHooks.
|
|
347
|
+
this.leaveCallHooks.add(registerRingingCallEventHandlers(this));
|
|
341
348
|
}),
|
|
342
349
|
);
|
|
343
350
|
}
|
|
@@ -815,7 +822,7 @@ export class Call {
|
|
|
815
822
|
},
|
|
816
823
|
);
|
|
817
824
|
|
|
818
|
-
this.leaveCallHooks.
|
|
825
|
+
this.leaveCallHooks.add(() => {
|
|
819
826
|
unsubscribeOnlineEvent();
|
|
820
827
|
unsubscribeOfflineEvent();
|
|
821
828
|
});
|
|
@@ -904,7 +911,10 @@ export class Call {
|
|
|
904
911
|
return currentParticipants.map((p) => {
|
|
905
912
|
const participant: StreamVideoParticipant = Object.assign(p, {
|
|
906
913
|
isLocalParticipant: p.sessionId === sfuClient.sessionId,
|
|
907
|
-
viewportVisibilityState:
|
|
914
|
+
viewportVisibilityState: {
|
|
915
|
+
videoTrack: VisibilityState.UNKNOWN,
|
|
916
|
+
screenShareTrack: VisibilityState.UNKNOWN,
|
|
917
|
+
},
|
|
908
918
|
});
|
|
909
919
|
// We need to preserve the local state of the participant
|
|
910
920
|
// (e.g. videoDimension, visibilityState, pinnedAt, etc.)
|
|
@@ -1108,22 +1118,42 @@ export class Call {
|
|
|
1108
1118
|
* You have to create a subscription for each participant for all the different kinds of tracks you want to receive.
|
|
1109
1119
|
* You can only subscribe for tracks after the participant started publishing the given kind of track.
|
|
1110
1120
|
*
|
|
1111
|
-
* @param
|
|
1121
|
+
* @param trackType the kind of subscription to update.
|
|
1112
1122
|
* @param changes the list of subscription changes to do.
|
|
1113
1123
|
* @param type the debounce type to use for the update.
|
|
1114
1124
|
*/
|
|
1115
1125
|
updateSubscriptionsPartial = (
|
|
1116
|
-
|
|
1126
|
+
trackType: VideoTrackType | 'video' | 'screen',
|
|
1117
1127
|
changes: SubscriptionChanges,
|
|
1118
1128
|
type: DebounceType = DebounceType.SLOW,
|
|
1119
1129
|
) => {
|
|
1130
|
+
if (trackType === 'video') {
|
|
1131
|
+
this.logger(
|
|
1132
|
+
'warn',
|
|
1133
|
+
`updateSubscriptionsPartial: ${trackType} is deprecated. Please switch to 'videoTrack'`,
|
|
1134
|
+
);
|
|
1135
|
+
trackType = 'videoTrack';
|
|
1136
|
+
} else if (trackType === 'screen') {
|
|
1137
|
+
this.logger(
|
|
1138
|
+
'warn',
|
|
1139
|
+
`updateSubscriptionsPartial: ${trackType} is deprecated. Please switch to 'screenShareTrack'`,
|
|
1140
|
+
);
|
|
1141
|
+
trackType = 'screenShareTrack';
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1120
1144
|
const participants = this.state.updateParticipants(
|
|
1121
1145
|
Object.entries(changes).reduce<StreamVideoParticipantPatches>(
|
|
1122
1146
|
(acc, [sessionId, change]) => {
|
|
1147
|
+
if (change.dimension?.height) {
|
|
1148
|
+
change.dimension.height = Math.ceil(change.dimension.height);
|
|
1149
|
+
}
|
|
1150
|
+
if (change.dimension?.width) {
|
|
1151
|
+
change.dimension.width = Math.ceil(change.dimension.width);
|
|
1152
|
+
}
|
|
1123
1153
|
const prop: keyof StreamVideoParticipant | undefined =
|
|
1124
|
-
|
|
1154
|
+
trackType === 'videoTrack'
|
|
1125
1155
|
? 'videoDimension'
|
|
1126
|
-
:
|
|
1156
|
+
: trackType === 'screenShareTrack'
|
|
1127
1157
|
? 'screenShareDimension'
|
|
1128
1158
|
: undefined;
|
|
1129
1159
|
if (prop) {
|
|
@@ -1147,9 +1177,9 @@ export class Call {
|
|
|
1147
1177
|
type: DebounceType = DebounceType.SLOW,
|
|
1148
1178
|
) => {
|
|
1149
1179
|
const subscriptions: TrackSubscriptionDetails[] = [];
|
|
1150
|
-
|
|
1180
|
+
for (const p of participants) {
|
|
1151
1181
|
// we don't want to subscribe to our own tracks
|
|
1152
|
-
if (p.isLocalParticipant)
|
|
1182
|
+
if (p.isLocalParticipant) continue;
|
|
1153
1183
|
|
|
1154
1184
|
// NOTE: audio tracks don't have to be requested explicitly
|
|
1155
1185
|
// as the SFU will implicitly subscribe us to all of them,
|
|
@@ -1174,7 +1204,7 @@ export class Call {
|
|
|
1174
1204
|
dimension: p.screenShareDimension,
|
|
1175
1205
|
});
|
|
1176
1206
|
}
|
|
1177
|
-
}
|
|
1207
|
+
}
|
|
1178
1208
|
// schedule update
|
|
1179
1209
|
this.trackSubscriptionsSubject.next({ type, data: subscriptions });
|
|
1180
1210
|
};
|
|
@@ -1680,7 +1710,7 @@ export class Call {
|
|
|
1680
1710
|
)
|
|
1681
1711
|
.subscribe();
|
|
1682
1712
|
|
|
1683
|
-
this.leaveCallHooks.
|
|
1713
|
+
this.leaveCallHooks.add(() => {
|
|
1684
1714
|
!subscription.closed && subscription.unsubscribe();
|
|
1685
1715
|
});
|
|
1686
1716
|
};
|
|
@@ -1800,4 +1830,90 @@ export class Call {
|
|
|
1800
1830
|
await this.microphone.enable();
|
|
1801
1831
|
}
|
|
1802
1832
|
}
|
|
1833
|
+
|
|
1834
|
+
/**
|
|
1835
|
+
* Will begin tracking the given element for visibility changes within the
|
|
1836
|
+
* configured viewport element (`call.setViewport`).
|
|
1837
|
+
*
|
|
1838
|
+
* @param element the element to track.
|
|
1839
|
+
* @param sessionId the session id.
|
|
1840
|
+
* @param trackType the video mode.
|
|
1841
|
+
*/
|
|
1842
|
+
trackElementVisibility = <T extends HTMLElement>(
|
|
1843
|
+
element: T,
|
|
1844
|
+
sessionId: string,
|
|
1845
|
+
trackType: VideoTrackType,
|
|
1846
|
+
) => {
|
|
1847
|
+
return this.dynascaleManager.trackElementVisibility(
|
|
1848
|
+
element,
|
|
1849
|
+
sessionId,
|
|
1850
|
+
trackType,
|
|
1851
|
+
);
|
|
1852
|
+
};
|
|
1853
|
+
|
|
1854
|
+
/**
|
|
1855
|
+
* Sets the viewport element to track bound video elements for visibility.
|
|
1856
|
+
*
|
|
1857
|
+
* @param element the viewport element.
|
|
1858
|
+
*/
|
|
1859
|
+
setViewport = <T extends HTMLElement>(element: T) => {
|
|
1860
|
+
return this.dynascaleManager.setViewport(element);
|
|
1861
|
+
};
|
|
1862
|
+
|
|
1863
|
+
/**
|
|
1864
|
+
* Binds a DOM <video> element to the given session id.
|
|
1865
|
+
* This method will make sure that the video element will play
|
|
1866
|
+
* the correct video stream for the given session id.
|
|
1867
|
+
*
|
|
1868
|
+
* Under the hood, it would also keep track of the video element dimensions
|
|
1869
|
+
* and update the subscription accordingly in order to optimize the bandwidth.
|
|
1870
|
+
*
|
|
1871
|
+
* If a "viewport" is configured, the video element will be automatically
|
|
1872
|
+
* tracked for visibility and the subscription will be updated accordingly.
|
|
1873
|
+
*
|
|
1874
|
+
* @param videoElement the video element to bind to.
|
|
1875
|
+
* @param sessionId the session id.
|
|
1876
|
+
* @param trackType the kind of video.
|
|
1877
|
+
*/
|
|
1878
|
+
bindVideoElement = (
|
|
1879
|
+
videoElement: HTMLVideoElement,
|
|
1880
|
+
sessionId: string,
|
|
1881
|
+
trackType: VideoTrackType,
|
|
1882
|
+
) => {
|
|
1883
|
+
const unbind = this.dynascaleManager.bindVideoElement(
|
|
1884
|
+
videoElement,
|
|
1885
|
+
sessionId,
|
|
1886
|
+
trackType,
|
|
1887
|
+
);
|
|
1888
|
+
|
|
1889
|
+
if (!unbind) return;
|
|
1890
|
+
this.leaveCallHooks.add(unbind);
|
|
1891
|
+
return () => {
|
|
1892
|
+
this.leaveCallHooks.delete(unbind);
|
|
1893
|
+
unbind();
|
|
1894
|
+
};
|
|
1895
|
+
};
|
|
1896
|
+
|
|
1897
|
+
/**
|
|
1898
|
+
* Binds a DOM <audio> element to the given session id.
|
|
1899
|
+
*
|
|
1900
|
+
* This method will make sure that the audio element will
|
|
1901
|
+
* play the correct audio stream for the given session id.
|
|
1902
|
+
*
|
|
1903
|
+
* @param audioElement the audio element to bind to.
|
|
1904
|
+
* @param sessionId the session id.
|
|
1905
|
+
*/
|
|
1906
|
+
bindAudioElement = (audioElement: HTMLAudioElement, sessionId: string) => {
|
|
1907
|
+
const unbind = this.dynascaleManager.bindAudioElement(
|
|
1908
|
+
audioElement,
|
|
1909
|
+
sessionId,
|
|
1910
|
+
);
|
|
1911
|
+
|
|
1912
|
+
if (!unbind) return;
|
|
1913
|
+
this.leaveCallHooks.add(unbind);
|
|
1914
|
+
return () => {
|
|
1915
|
+
this.leaveCallHooks.delete(unbind);
|
|
1916
|
+
unbind();
|
|
1917
|
+
};
|
|
1918
|
+
};
|
|
1803
1919
|
}
|
|
@@ -34,7 +34,10 @@ describe('Participant events', () => {
|
|
|
34
34
|
{
|
|
35
35
|
userId: 'user-id',
|
|
36
36
|
sessionId: 'session-id',
|
|
37
|
-
viewportVisibilityState:
|
|
37
|
+
viewportVisibilityState: {
|
|
38
|
+
videoTrack: VisibilityState.UNKNOWN,
|
|
39
|
+
screenShareTrack: VisibilityState.UNKNOWN,
|
|
40
|
+
},
|
|
38
41
|
},
|
|
39
42
|
]);
|
|
40
43
|
|
|
@@ -23,7 +23,10 @@ export const watchParticipantJoined = (state: CallState) => {
|
|
|
23
23
|
Object.assign<StreamVideoParticipant, Partial<StreamVideoParticipant>>(
|
|
24
24
|
participant,
|
|
25
25
|
{
|
|
26
|
-
viewportVisibilityState:
|
|
26
|
+
viewportVisibilityState: {
|
|
27
|
+
videoTrack: VisibilityState.UNKNOWN,
|
|
28
|
+
screenShareTrack: VisibilityState.UNKNOWN,
|
|
29
|
+
},
|
|
27
30
|
},
|
|
28
31
|
),
|
|
29
32
|
);
|