@stream-io/video-client 1.7.4 → 1.8.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.
- package/CHANGELOG.md +7 -0
- package/dist/index.browser.es.js +174 -91
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +173 -90
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +174 -91
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +18 -14
- package/dist/src/helpers/DynascaleManager.d.ts +41 -4
- package/dist/src/store/CallState.d.ts +11 -1
- package/package.json +1 -1
- package/src/Call.ts +45 -121
- package/src/helpers/DynascaleManager.ts +176 -20
- package/src/helpers/__tests__/DynascaleManager.test.ts +79 -112
- package/src/store/CallState.ts +43 -3
package/dist/index.cjs.js
CHANGED
|
@@ -3041,7 +3041,7 @@ const retryable = async (rpc, signal) => {
|
|
|
3041
3041
|
return result;
|
|
3042
3042
|
};
|
|
3043
3043
|
|
|
3044
|
-
const version = "1.
|
|
3044
|
+
const version = "1.8.0";
|
|
3045
3045
|
const [major, minor, patch] = version.split('.');
|
|
3046
3046
|
let sdkInfo = {
|
|
3047
3047
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -4312,6 +4312,34 @@ class CallState {
|
|
|
4312
4312
|
return p;
|
|
4313
4313
|
}));
|
|
4314
4314
|
};
|
|
4315
|
+
/**
|
|
4316
|
+
* Update track subscription configuration for one or more participants.
|
|
4317
|
+
* You have to create a subscription for each participant for all the different kinds of tracks you want to receive.
|
|
4318
|
+
* You can only subscribe for tracks after the participant started publishing the given kind of track.
|
|
4319
|
+
*
|
|
4320
|
+
* @param trackType the kind of subscription to update.
|
|
4321
|
+
* @param changes the list of subscription changes to do.
|
|
4322
|
+
* @param type the debounce type to use for the update.
|
|
4323
|
+
*/
|
|
4324
|
+
this.updateParticipantTracks = (trackType, changes) => {
|
|
4325
|
+
return this.updateParticipants(Object.entries(changes).reduce((acc, [sessionId, change]) => {
|
|
4326
|
+
if (change.dimension) {
|
|
4327
|
+
change.dimension.height = Math.ceil(change.dimension.height);
|
|
4328
|
+
change.dimension.width = Math.ceil(change.dimension.width);
|
|
4329
|
+
}
|
|
4330
|
+
const prop = trackType === 'videoTrack'
|
|
4331
|
+
? 'videoDimension'
|
|
4332
|
+
: trackType === 'screenShareTrack'
|
|
4333
|
+
? 'screenShareDimension'
|
|
4334
|
+
: undefined;
|
|
4335
|
+
if (prop) {
|
|
4336
|
+
acc[sessionId] = {
|
|
4337
|
+
[prop]: change.dimension,
|
|
4338
|
+
};
|
|
4339
|
+
}
|
|
4340
|
+
return acc;
|
|
4341
|
+
}, {}));
|
|
4342
|
+
};
|
|
4315
4343
|
/**
|
|
4316
4344
|
* Updates the call state with the data received from the server.
|
|
4317
4345
|
*
|
|
@@ -7092,6 +7120,7 @@ const DEFAULT_VIEWPORT_VISIBILITY_STATE = {
|
|
|
7092
7120
|
videoTrack: exports.VisibilityState.UNKNOWN,
|
|
7093
7121
|
screenShareTrack: exports.VisibilityState.UNKNOWN,
|
|
7094
7122
|
};
|
|
7123
|
+
const globalOverrideKey = Symbol('globalOverrideKey');
|
|
7095
7124
|
/**
|
|
7096
7125
|
* A manager class that handles dynascale related tasks like:
|
|
7097
7126
|
*
|
|
@@ -7108,12 +7137,64 @@ class DynascaleManager {
|
|
|
7108
7137
|
*
|
|
7109
7138
|
* @param call the call to manage.
|
|
7110
7139
|
*/
|
|
7111
|
-
constructor(
|
|
7140
|
+
constructor(callState, speaker) {
|
|
7112
7141
|
/**
|
|
7113
7142
|
* The viewport tracker instance.
|
|
7114
7143
|
*/
|
|
7115
7144
|
this.viewportTracker = new ViewportTracker();
|
|
7116
7145
|
this.logger = getLogger(['DynascaleManager']);
|
|
7146
|
+
this.pendingSubscriptionsUpdate = null;
|
|
7147
|
+
this.videoTrackSubscriptionOverridesSubject = new rxjs.BehaviorSubject({});
|
|
7148
|
+
this.videoTrackSubscriptionOverrides$ = this.videoTrackSubscriptionOverridesSubject.asObservable();
|
|
7149
|
+
this.incomingVideoSettings$ = this.videoTrackSubscriptionOverrides$.pipe(rxjs.map((overrides) => {
|
|
7150
|
+
const { [globalOverrideKey]: globalSettings, ...participants } = overrides;
|
|
7151
|
+
return {
|
|
7152
|
+
enabled: globalSettings?.enabled !== false,
|
|
7153
|
+
preferredResolution: globalSettings?.enabled
|
|
7154
|
+
? globalSettings.dimension
|
|
7155
|
+
: undefined,
|
|
7156
|
+
participants: Object.fromEntries(Object.entries(participants).map(([sessionId, participantOverride]) => [
|
|
7157
|
+
sessionId,
|
|
7158
|
+
{
|
|
7159
|
+
enabled: participantOverride?.enabled !== false,
|
|
7160
|
+
preferredResolution: participantOverride?.enabled
|
|
7161
|
+
? participantOverride.dimension
|
|
7162
|
+
: undefined,
|
|
7163
|
+
},
|
|
7164
|
+
])),
|
|
7165
|
+
isParticipantVideoEnabled: (sessionId) => overrides[sessionId]?.enabled ??
|
|
7166
|
+
overrides[globalOverrideKey]?.enabled ??
|
|
7167
|
+
true,
|
|
7168
|
+
};
|
|
7169
|
+
}), rxjs.shareReplay(1));
|
|
7170
|
+
this.setVideoTrackSubscriptionOverrides = (override, sessionIds) => {
|
|
7171
|
+
if (!sessionIds) {
|
|
7172
|
+
return setCurrentValue(this.videoTrackSubscriptionOverridesSubject, override ? { [globalOverrideKey]: override } : {});
|
|
7173
|
+
}
|
|
7174
|
+
return setCurrentValue(this.videoTrackSubscriptionOverridesSubject, (overrides) => ({
|
|
7175
|
+
...overrides,
|
|
7176
|
+
...Object.fromEntries(sessionIds.map((id) => [id, override])),
|
|
7177
|
+
}));
|
|
7178
|
+
};
|
|
7179
|
+
this.applyTrackSubscriptions = (debounceType = exports.DebounceType.SLOW) => {
|
|
7180
|
+
if (this.pendingSubscriptionsUpdate) {
|
|
7181
|
+
clearTimeout(this.pendingSubscriptionsUpdate);
|
|
7182
|
+
}
|
|
7183
|
+
const updateSubscriptions = () => {
|
|
7184
|
+
this.pendingSubscriptionsUpdate = null;
|
|
7185
|
+
this.sfuClient
|
|
7186
|
+
?.updateSubscriptions(this.trackSubscriptions)
|
|
7187
|
+
.catch((err) => {
|
|
7188
|
+
this.logger('debug', `Failed to update track subscriptions`, err);
|
|
7189
|
+
});
|
|
7190
|
+
};
|
|
7191
|
+
if (debounceType) {
|
|
7192
|
+
this.pendingSubscriptionsUpdate = setTimeout(updateSubscriptions, debounceType);
|
|
7193
|
+
}
|
|
7194
|
+
else {
|
|
7195
|
+
updateSubscriptions();
|
|
7196
|
+
}
|
|
7197
|
+
};
|
|
7117
7198
|
/**
|
|
7118
7199
|
* Will begin tracking the given element for visibility changes within the
|
|
7119
7200
|
* configured viewport element (`call.setViewport`).
|
|
@@ -7125,7 +7206,7 @@ class DynascaleManager {
|
|
|
7125
7206
|
*/
|
|
7126
7207
|
this.trackElementVisibility = (element, sessionId, trackType) => {
|
|
7127
7208
|
const cleanup = this.viewportTracker.observe(element, (entry) => {
|
|
7128
|
-
this.
|
|
7209
|
+
this.callState.updateParticipant(sessionId, (participant) => {
|
|
7129
7210
|
const previousVisibilityState = participant.viewportVisibilityState ??
|
|
7130
7211
|
DEFAULT_VIEWPORT_VISIBILITY_STATE;
|
|
7131
7212
|
// observer triggers when the element is "moved" to be a fullscreen element
|
|
@@ -7147,7 +7228,7 @@ class DynascaleManager {
|
|
|
7147
7228
|
// reset visibility state to UNKNOWN upon cleanup
|
|
7148
7229
|
// so that the layouts that are not actively observed
|
|
7149
7230
|
// can still function normally (runtime layout switching)
|
|
7150
|
-
this.
|
|
7231
|
+
this.callState.updateParticipant(sessionId, (participant) => {
|
|
7151
7232
|
const previousVisibilityState = participant.viewportVisibilityState ??
|
|
7152
7233
|
DEFAULT_VIEWPORT_VISIBILITY_STATE;
|
|
7153
7234
|
return {
|
|
@@ -7184,7 +7265,7 @@ class DynascaleManager {
|
|
|
7184
7265
|
* @param trackType the kind of video.
|
|
7185
7266
|
*/
|
|
7186
7267
|
this.bindVideoElement = (videoElement, sessionId, trackType) => {
|
|
7187
|
-
const boundParticipant = this.
|
|
7268
|
+
const boundParticipant = this.callState.findParticipantBySessionId(sessionId);
|
|
7188
7269
|
if (!boundParticipant)
|
|
7189
7270
|
return;
|
|
7190
7271
|
const requestTrackWithDimensions = (debounceType, dimension) => {
|
|
@@ -7196,9 +7277,12 @@ class DynascaleManager {
|
|
|
7196
7277
|
this.logger('debug', `Ignoring 0x0 dimension`, boundParticipant);
|
|
7197
7278
|
dimension = undefined;
|
|
7198
7279
|
}
|
|
7199
|
-
this.
|
|
7280
|
+
this.callState.updateParticipantTracks(trackType, {
|
|
7281
|
+
[sessionId]: { dimension },
|
|
7282
|
+
});
|
|
7283
|
+
this.applyTrackSubscriptions(debounceType);
|
|
7200
7284
|
};
|
|
7201
|
-
const participant$ = this.
|
|
7285
|
+
const participant$ = this.callState.participants$.pipe(rxjs.map((participants) => participants.find((participant) => participant.sessionId === sessionId)), rxjs.takeWhile((participant) => !!participant), rxjs.distinctUntilChanged(), rxjs.shareReplay({ bufferSize: 1, refCount: true }));
|
|
7202
7286
|
/**
|
|
7203
7287
|
* Since the video elements are now being removed from the DOM (React SDK) upon
|
|
7204
7288
|
* visibility change, this subscription is not in use an stays here only for the
|
|
@@ -7313,10 +7397,10 @@ class DynascaleManager {
|
|
|
7313
7397
|
* @returns a cleanup function that will unbind the audio element.
|
|
7314
7398
|
*/
|
|
7315
7399
|
this.bindAudioElement = (audioElement, sessionId, trackType) => {
|
|
7316
|
-
const participant = this.
|
|
7400
|
+
const participant = this.callState.findParticipantBySessionId(sessionId);
|
|
7317
7401
|
if (!participant || participant.isLocalParticipant)
|
|
7318
7402
|
return;
|
|
7319
|
-
const participant$ = this.
|
|
7403
|
+
const participant$ = this.callState.participants$.pipe(rxjs.map((participants) => participants.find((p) => p.sessionId === sessionId)), rxjs.takeWhile((p) => !!p), rxjs.distinctUntilChanged(), rxjs.shareReplay({ bufferSize: 1, refCount: true }));
|
|
7320
7404
|
const updateMediaStreamSubscription = participant$
|
|
7321
7405
|
.pipe(rxjs.distinctUntilKeyChanged(trackType === 'screenShareAudioTrack'
|
|
7322
7406
|
? 'screenShareAudioStream'
|
|
@@ -7336,7 +7420,7 @@ class DynascaleManager {
|
|
|
7336
7420
|
// audio output device shall be set after the audio element is played
|
|
7337
7421
|
// otherwise, the browser will not pick it up, and will always
|
|
7338
7422
|
// play audio through the system's default device
|
|
7339
|
-
const { selectedDevice } = this.
|
|
7423
|
+
const { selectedDevice } = this.speaker.state;
|
|
7340
7424
|
if (selectedDevice && 'setSinkId' in audioElement) {
|
|
7341
7425
|
audioElement.setSinkId(selectedDevice);
|
|
7342
7426
|
}
|
|
@@ -7345,13 +7429,13 @@ class DynascaleManager {
|
|
|
7345
7429
|
});
|
|
7346
7430
|
const sinkIdSubscription = !('setSinkId' in audioElement)
|
|
7347
7431
|
? null
|
|
7348
|
-
: this.
|
|
7432
|
+
: this.speaker.state.selectedDevice$.subscribe((deviceId) => {
|
|
7349
7433
|
if (deviceId) {
|
|
7350
7434
|
audioElement.setSinkId(deviceId);
|
|
7351
7435
|
}
|
|
7352
7436
|
});
|
|
7353
7437
|
const volumeSubscription = rxjs.combineLatest([
|
|
7354
|
-
this.
|
|
7438
|
+
this.speaker.state.volume$,
|
|
7355
7439
|
participant$.pipe(rxjs.distinctUntilKeyChanged('audioVolume')),
|
|
7356
7440
|
]).subscribe(([volume, p]) => {
|
|
7357
7441
|
audioElement.volume = p.audioVolume ?? volume;
|
|
@@ -7363,7 +7447,50 @@ class DynascaleManager {
|
|
|
7363
7447
|
updateMediaStreamSubscription.unsubscribe();
|
|
7364
7448
|
};
|
|
7365
7449
|
};
|
|
7366
|
-
this.
|
|
7450
|
+
this.callState = callState;
|
|
7451
|
+
this.speaker = speaker;
|
|
7452
|
+
}
|
|
7453
|
+
setSfuClient(sfuClient) {
|
|
7454
|
+
this.sfuClient = sfuClient;
|
|
7455
|
+
}
|
|
7456
|
+
get trackSubscriptions() {
|
|
7457
|
+
const subscriptions = [];
|
|
7458
|
+
for (const p of this.callState.remoteParticipants) {
|
|
7459
|
+
// NOTE: audio tracks don't have to be requested explicitly
|
|
7460
|
+
// as the SFU will implicitly subscribe us to all of them,
|
|
7461
|
+
// once they become available.
|
|
7462
|
+
if (p.videoDimension && hasVideo(p)) {
|
|
7463
|
+
const override = this.videoTrackSubscriptionOverrides[p.sessionId] ??
|
|
7464
|
+
this.videoTrackSubscriptionOverrides[globalOverrideKey];
|
|
7465
|
+
if (override?.enabled !== false) {
|
|
7466
|
+
subscriptions.push({
|
|
7467
|
+
userId: p.userId,
|
|
7468
|
+
sessionId: p.sessionId,
|
|
7469
|
+
trackType: TrackType.VIDEO,
|
|
7470
|
+
dimension: override?.dimension ?? p.videoDimension,
|
|
7471
|
+
});
|
|
7472
|
+
}
|
|
7473
|
+
}
|
|
7474
|
+
if (p.screenShareDimension && hasScreenShare(p)) {
|
|
7475
|
+
subscriptions.push({
|
|
7476
|
+
userId: p.userId,
|
|
7477
|
+
sessionId: p.sessionId,
|
|
7478
|
+
trackType: TrackType.SCREEN_SHARE,
|
|
7479
|
+
dimension: p.screenShareDimension,
|
|
7480
|
+
});
|
|
7481
|
+
}
|
|
7482
|
+
if (hasScreenShareAudio(p)) {
|
|
7483
|
+
subscriptions.push({
|
|
7484
|
+
userId: p.userId,
|
|
7485
|
+
sessionId: p.sessionId,
|
|
7486
|
+
trackType: TrackType.SCREEN_SHARE_AUDIO,
|
|
7487
|
+
});
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7490
|
+
return subscriptions;
|
|
7491
|
+
}
|
|
7492
|
+
get videoTrackSubscriptionOverrides() {
|
|
7493
|
+
return getCurrentValue(this.videoTrackSubscriptionOverrides$);
|
|
7367
7494
|
}
|
|
7368
7495
|
}
|
|
7369
7496
|
|
|
@@ -9182,10 +9309,6 @@ class Call {
|
|
|
9182
9309
|
* The state of this call.
|
|
9183
9310
|
*/
|
|
9184
9311
|
this.state = new CallState();
|
|
9185
|
-
/**
|
|
9186
|
-
* The DynascaleManager instance.
|
|
9187
|
-
*/
|
|
9188
|
-
this.dynascaleManager = new DynascaleManager(this);
|
|
9189
9312
|
/**
|
|
9190
9313
|
* The permissions context of this call.
|
|
9191
9314
|
*/
|
|
@@ -9195,7 +9318,6 @@ class Call {
|
|
|
9195
9318
|
* @private
|
|
9196
9319
|
*/
|
|
9197
9320
|
this.dispatcher = new Dispatcher();
|
|
9198
|
-
this.trackSubscriptionsSubject = new rxjs.BehaviorSubject({ type: exports.DebounceType.MEDIUM, data: [] });
|
|
9199
9321
|
this.sfuClientTag = 0;
|
|
9200
9322
|
this.reconnectConcurrencyTag = Symbol('reconnectConcurrencyTag');
|
|
9201
9323
|
this.reconnectAttempts = 0;
|
|
@@ -9336,6 +9458,7 @@ class Call {
|
|
|
9336
9458
|
this.publisher = undefined;
|
|
9337
9459
|
await this.sfuClient?.leaveAndClose(reason);
|
|
9338
9460
|
this.sfuClient = undefined;
|
|
9461
|
+
this.dynascaleManager.setSfuClient(undefined);
|
|
9339
9462
|
this.state.setCallingState(exports.CallingState.LEFT);
|
|
9340
9463
|
// Call all leave call hooks, e.g. to clean up global event handlers
|
|
9341
9464
|
this.leaveCallHooks.forEach((hook) => hook());
|
|
@@ -9498,6 +9621,7 @@ class Call {
|
|
|
9498
9621
|
})
|
|
9499
9622
|
: previousSfuClient;
|
|
9500
9623
|
this.sfuClient = sfuClient;
|
|
9624
|
+
this.dynascaleManager.setSfuClient(sfuClient);
|
|
9501
9625
|
const clientDetails = getClientDetails();
|
|
9502
9626
|
// we don't need to send JoinRequest if we are re-using an existing healthy SFU client
|
|
9503
9627
|
if (previousSfuClient !== sfuClient) {
|
|
@@ -9570,11 +9694,10 @@ class Call {
|
|
|
9570
9694
|
const strategy = this.reconnectStrategy;
|
|
9571
9695
|
const performingRejoin = strategy === WebsocketReconnectStrategy.REJOIN;
|
|
9572
9696
|
const announcedTracks = this.publisher?.getAnnouncedTracks() || [];
|
|
9573
|
-
const subscribedTracks = getCurrentValue(this.trackSubscriptionsSubject);
|
|
9574
9697
|
return {
|
|
9575
9698
|
strategy,
|
|
9576
9699
|
announcedTracks,
|
|
9577
|
-
subscriptions:
|
|
9700
|
+
subscriptions: this.dynascaleManager.trackSubscriptions,
|
|
9578
9701
|
reconnectAttempt: this.reconnectAttempts,
|
|
9579
9702
|
fromSfuId: migratingFromSfuId || '',
|
|
9580
9703
|
previousSessionId: performingRejoin ? previousSessionId || '' : '',
|
|
@@ -9944,7 +10067,7 @@ class Call {
|
|
|
9944
10067
|
const { remoteParticipants } = this.state;
|
|
9945
10068
|
if (remoteParticipants.length <= 0)
|
|
9946
10069
|
return;
|
|
9947
|
-
this.
|
|
10070
|
+
this.dynascaleManager.applyTrackSubscriptions(undefined);
|
|
9948
10071
|
};
|
|
9949
10072
|
/**
|
|
9950
10073
|
* Starts publishing the given video stream to the call.
|
|
@@ -10067,71 +10190,6 @@ class Call {
|
|
|
10067
10190
|
this.logger('warn', 'Failed to notify stop of noise cancellation', err);
|
|
10068
10191
|
});
|
|
10069
10192
|
};
|
|
10070
|
-
/**
|
|
10071
|
-
* Update track subscription configuration for one or more participants.
|
|
10072
|
-
* You have to create a subscription for each participant for all the different kinds of tracks you want to receive.
|
|
10073
|
-
* You can only subscribe for tracks after the participant started publishing the given kind of track.
|
|
10074
|
-
*
|
|
10075
|
-
* @param trackType the kind of subscription to update.
|
|
10076
|
-
* @param changes the list of subscription changes to do.
|
|
10077
|
-
* @param type the debounce type to use for the update.
|
|
10078
|
-
*/
|
|
10079
|
-
this.updateSubscriptionsPartial = (trackType, changes, type = exports.DebounceType.SLOW) => {
|
|
10080
|
-
const participants = this.state.updateParticipants(Object.entries(changes).reduce((acc, [sessionId, change]) => {
|
|
10081
|
-
if (change.dimension) {
|
|
10082
|
-
change.dimension.height = Math.ceil(change.dimension.height);
|
|
10083
|
-
change.dimension.width = Math.ceil(change.dimension.width);
|
|
10084
|
-
}
|
|
10085
|
-
const prop = trackType === 'videoTrack'
|
|
10086
|
-
? 'videoDimension'
|
|
10087
|
-
: trackType === 'screenShareTrack'
|
|
10088
|
-
? 'screenShareDimension'
|
|
10089
|
-
: undefined;
|
|
10090
|
-
if (prop) {
|
|
10091
|
-
acc[sessionId] = {
|
|
10092
|
-
[prop]: change.dimension,
|
|
10093
|
-
};
|
|
10094
|
-
}
|
|
10095
|
-
return acc;
|
|
10096
|
-
}, {}));
|
|
10097
|
-
this.updateSubscriptions(participants, type);
|
|
10098
|
-
};
|
|
10099
|
-
this.updateSubscriptions = (participants, type = exports.DebounceType.SLOW) => {
|
|
10100
|
-
const subscriptions = [];
|
|
10101
|
-
for (const p of participants) {
|
|
10102
|
-
// we don't want to subscribe to our own tracks
|
|
10103
|
-
if (p.isLocalParticipant)
|
|
10104
|
-
continue;
|
|
10105
|
-
// NOTE: audio tracks don't have to be requested explicitly
|
|
10106
|
-
// as the SFU will implicitly subscribe us to all of them,
|
|
10107
|
-
// once they become available.
|
|
10108
|
-
if (p.videoDimension && hasVideo(p)) {
|
|
10109
|
-
subscriptions.push({
|
|
10110
|
-
userId: p.userId,
|
|
10111
|
-
sessionId: p.sessionId,
|
|
10112
|
-
trackType: TrackType.VIDEO,
|
|
10113
|
-
dimension: p.videoDimension,
|
|
10114
|
-
});
|
|
10115
|
-
}
|
|
10116
|
-
if (p.screenShareDimension && hasScreenShare(p)) {
|
|
10117
|
-
subscriptions.push({
|
|
10118
|
-
userId: p.userId,
|
|
10119
|
-
sessionId: p.sessionId,
|
|
10120
|
-
trackType: TrackType.SCREEN_SHARE,
|
|
10121
|
-
dimension: p.screenShareDimension,
|
|
10122
|
-
});
|
|
10123
|
-
}
|
|
10124
|
-
if (hasScreenShareAudio(p)) {
|
|
10125
|
-
subscriptions.push({
|
|
10126
|
-
userId: p.userId,
|
|
10127
|
-
sessionId: p.sessionId,
|
|
10128
|
-
trackType: TrackType.SCREEN_SHARE_AUDIO,
|
|
10129
|
-
});
|
|
10130
|
-
}
|
|
10131
|
-
}
|
|
10132
|
-
// schedule update
|
|
10133
|
-
this.trackSubscriptionsSubject.next({ type, data: subscriptions });
|
|
10134
|
-
};
|
|
10135
10193
|
/**
|
|
10136
10194
|
* Will enhance the reported stats with additional participant-specific information (`callStatsReport$` state [store variable](./StreamVideoClient.md/#readonlystatestore)).
|
|
10137
10195
|
* This is usually helpful when detailed stats for a specific participant are needed.
|
|
@@ -10711,6 +10769,33 @@ class Call {
|
|
|
10711
10769
|
imageElement.removeEventListener('error', handleError);
|
|
10712
10770
|
};
|
|
10713
10771
|
};
|
|
10772
|
+
/**
|
|
10773
|
+
* Specify preference for incoming video resolution. The preference will
|
|
10774
|
+
* be matched as close as possible, but actual resolution will depend
|
|
10775
|
+
* on the video source quality and client network conditions. Will enable
|
|
10776
|
+
* incoming video, if previously disabled.
|
|
10777
|
+
*
|
|
10778
|
+
* @param resolution preferred resolution, or `undefined` to clear preference
|
|
10779
|
+
* @param sessionIds optionally specify session ids of the participants this
|
|
10780
|
+
* preference has effect on. Affects all participants by default.
|
|
10781
|
+
*/
|
|
10782
|
+
this.setPreferredIncomingVideoResolution = (resolution, sessionIds) => {
|
|
10783
|
+
this.dynascaleManager.setVideoTrackSubscriptionOverrides(resolution
|
|
10784
|
+
? {
|
|
10785
|
+
enabled: true,
|
|
10786
|
+
dimension: resolution,
|
|
10787
|
+
}
|
|
10788
|
+
: undefined, sessionIds);
|
|
10789
|
+
this.dynascaleManager.applyTrackSubscriptions();
|
|
10790
|
+
};
|
|
10791
|
+
/**
|
|
10792
|
+
* Enables or disables incoming video from all remote call participants,
|
|
10793
|
+
* and removes any preference for preferred resolution.
|
|
10794
|
+
*/
|
|
10795
|
+
this.setIncomingVideoEnabled = (enabled) => {
|
|
10796
|
+
this.dynascaleManager.setVideoTrackSubscriptionOverrides(enabled ? undefined : { enabled: false });
|
|
10797
|
+
this.dynascaleManager.applyTrackSubscriptions();
|
|
10798
|
+
};
|
|
10714
10799
|
this.type = type;
|
|
10715
10800
|
this.id = id;
|
|
10716
10801
|
this.cid = `${type}:${id}`;
|
|
@@ -10732,6 +10817,7 @@ class Call {
|
|
|
10732
10817
|
this.microphone = new MicrophoneManager(this);
|
|
10733
10818
|
this.speaker = new SpeakerManager(this);
|
|
10734
10819
|
this.screenShare = new ScreenShareManager(this);
|
|
10820
|
+
this.dynascaleManager = new DynascaleManager(this.state, this.speaker);
|
|
10735
10821
|
}
|
|
10736
10822
|
async setup() {
|
|
10737
10823
|
await withoutConcurrency(this.joinLeaveConcurrencyTag, async () => {
|
|
@@ -10744,9 +10830,6 @@ class Call {
|
|
|
10744
10830
|
this.leaveCallHooks.add(registerEventHandlers(this, this.dispatcher));
|
|
10745
10831
|
this.registerEffects();
|
|
10746
10832
|
this.registerReconnectHandlers();
|
|
10747
|
-
this.leaveCallHooks.add(createSubscription(this.trackSubscriptionsSubject.pipe(rxjs.debounce((v) => rxjs.timer(v.type)), rxjs.map((v) => v.data)), (subscriptions) => this.sfuClient?.updateSubscriptions(subscriptions).catch((err) => {
|
|
10748
|
-
this.logger('debug', `Failed to update track subscriptions`, err);
|
|
10749
|
-
})));
|
|
10750
10833
|
if (this.state.callingState === exports.CallingState.LEFT) {
|
|
10751
10834
|
this.state.setCallingState(exports.CallingState.IDLE);
|
|
10752
10835
|
}
|
|
@@ -12392,7 +12475,7 @@ class StreamClient {
|
|
|
12392
12475
|
});
|
|
12393
12476
|
};
|
|
12394
12477
|
this.getUserAgent = () => {
|
|
12395
|
-
const version = "1.
|
|
12478
|
+
const version = "1.8.0";
|
|
12396
12479
|
return (this.userAgent ||
|
|
12397
12480
|
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
|
|
12398
12481
|
};
|