@stream-io/video-client 1.28.1 → 1.30.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 +19 -0
- package/dist/index.browser.es.js +126 -31
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +125 -29
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +126 -31
- package/dist/index.es.js.map +1 -1
- package/dist/src/devices/InputMediaDeviceManager.d.ts +3 -0
- package/dist/src/gen/video/sfu/event/events.d.ts +5 -1
- package/dist/src/gen/video/sfu/models/models.d.ts +35 -0
- package/dist/src/sorting/participants.d.ts +8 -0
- package/dist/src/store/CallState.d.ts +9 -1
- package/package.json +1 -1
- package/src/Call.ts +2 -0
- package/src/__tests__/StreamVideoClient.api.test.ts +1 -1
- package/src/coordinator/connection/client.ts +9 -1
- package/src/devices/InputMediaDeviceManager.ts +8 -1
- package/src/events/call-permissions.ts +2 -20
- package/src/gen/video/sfu/event/events.ts +15 -0
- package/src/gen/video/sfu/models/models.ts +45 -0
- package/src/sorting/participants.ts +15 -0
- package/src/sorting/presets.ts +3 -4
- package/src/store/CallState.ts +59 -6
|
@@ -41,6 +41,9 @@ export declare abstract class InputMediaDeviceManager<T extends InputMediaDevice
|
|
|
41
41
|
* Stops or pauses the stream based on state.disableMode
|
|
42
42
|
* @param {boolean} [forceStop=false] when true, stops the tracks regardless of the state.disableMode
|
|
43
43
|
*/
|
|
44
|
+
disable(options: {
|
|
45
|
+
forceStop?: boolean;
|
|
46
|
+
}): Promise<void>;
|
|
44
47
|
disable(forceStop?: boolean): Promise<void>;
|
|
45
48
|
/**
|
|
46
49
|
* Returns a promise that resolves when all pe
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MessageType } from '@protobuf-ts/runtime';
|
|
2
|
-
import { CallEndedReason, CallGrants, CallState, ClientCapability, ClientDetails, Codec, ConnectionQuality, Error as Error$, GoAwayReason, ICETrickle as ICETrickle$, Participant, ParticipantCount, PeerType, Pin, PublishOption, SubscribeOption, TrackInfo, TrackType, TrackUnpublishReason, WebsocketReconnectStrategy } from '../models/models';
|
|
2
|
+
import { CallEndedReason, CallGrants, CallState, ClientCapability, ClientDetails, Codec, ConnectionQuality, Error as Error$, GoAwayReason, ICETrickle as ICETrickle$, Participant, ParticipantCount, ParticipantSource, PeerType, Pin, PublishOption, SubscribeOption, TrackInfo, TrackType, TrackUnpublishReason, WebsocketReconnectStrategy } from '../models/models';
|
|
3
3
|
import { TrackSubscriptionDetails } from '../signal_rpc/signal';
|
|
4
4
|
/**
|
|
5
5
|
* SFUEvent is a message that is sent from the SFU to the client.
|
|
@@ -472,6 +472,10 @@ export interface JoinRequest {
|
|
|
472
472
|
* @generated from protobuf field: repeated stream.video.sfu.models.ClientCapability capabilities = 11;
|
|
473
473
|
*/
|
|
474
474
|
capabilities: ClientCapability[];
|
|
475
|
+
/**
|
|
476
|
+
* @generated from protobuf field: stream.video.sfu.models.ParticipantSource source = 12;
|
|
477
|
+
*/
|
|
478
|
+
source: ParticipantSource;
|
|
475
479
|
}
|
|
476
480
|
/**
|
|
477
481
|
* @generated from protobuf message stream.video.sfu.event.ReconnectDetails
|
|
@@ -133,6 +133,10 @@ export interface Participant {
|
|
|
133
133
|
* @generated from protobuf field: repeated string roles = 13;
|
|
134
134
|
*/
|
|
135
135
|
roles: string[];
|
|
136
|
+
/**
|
|
137
|
+
* @generated from protobuf field: stream.video.sfu.models.ParticipantSource source = 14;
|
|
138
|
+
*/
|
|
139
|
+
source: ParticipantSource;
|
|
136
140
|
}
|
|
137
141
|
/**
|
|
138
142
|
* @generated from protobuf message stream.video.sfu.models.StreamQuality
|
|
@@ -755,6 +759,37 @@ export declare enum TrackType {
|
|
|
755
759
|
*/
|
|
756
760
|
SCREEN_SHARE_AUDIO = 4
|
|
757
761
|
}
|
|
762
|
+
/**
|
|
763
|
+
* must be aligned with kit
|
|
764
|
+
*
|
|
765
|
+
* @generated from protobuf enum stream.video.sfu.models.ParticipantSource
|
|
766
|
+
*/
|
|
767
|
+
export declare enum ParticipantSource {
|
|
768
|
+
/**
|
|
769
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_WEBRTC_UNSPECIFIED = 0;
|
|
770
|
+
*/
|
|
771
|
+
WEBRTC_UNSPECIFIED = 0,
|
|
772
|
+
/**
|
|
773
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_RTMP = 1;
|
|
774
|
+
*/
|
|
775
|
+
RTMP = 1,
|
|
776
|
+
/**
|
|
777
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_WHIP = 2;
|
|
778
|
+
*/
|
|
779
|
+
WHIP = 2,
|
|
780
|
+
/**
|
|
781
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_SIP = 3;
|
|
782
|
+
*/
|
|
783
|
+
SIP = 3,
|
|
784
|
+
/**
|
|
785
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_RTSP = 4;
|
|
786
|
+
*/
|
|
787
|
+
RTSP = 4,
|
|
788
|
+
/**
|
|
789
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_SRT = 5;
|
|
790
|
+
*/
|
|
791
|
+
SRT = 5
|
|
792
|
+
}
|
|
758
793
|
/**
|
|
759
794
|
* @generated from protobuf enum stream.video.sfu.models.ErrorCode
|
|
760
795
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Comparator } from './';
|
|
2
2
|
import { StreamVideoParticipant } from '../types';
|
|
3
|
+
import { ParticipantSource } from '../gen/video/sfu/models/models';
|
|
3
4
|
/**
|
|
4
5
|
* A comparator which sorts participants by the fact that they are the dominant speaker or not.
|
|
5
6
|
*
|
|
@@ -42,6 +43,13 @@ export declare const publishingAudio: Comparator<StreamVideoParticipant>;
|
|
|
42
43
|
* @param b the second participant.
|
|
43
44
|
*/
|
|
44
45
|
export declare const pinned: Comparator<StreamVideoParticipant>;
|
|
46
|
+
/**
|
|
47
|
+
* A comparator creator which will set up a comparator which prioritizes
|
|
48
|
+
* participants who are from a specific source (e.g., WebRTC, RTMP, WHIP...).
|
|
49
|
+
*
|
|
50
|
+
* @param source the source to prioritize.
|
|
51
|
+
*/
|
|
52
|
+
export declare const withParticipantSource: (source: ParticipantSource) => Comparator<StreamVideoParticipant>;
|
|
45
53
|
/**
|
|
46
54
|
* A comparator creator which will set up a comparator which prioritizes
|
|
47
55
|
* participants who have a specific reaction.
|
|
@@ -5,7 +5,7 @@ import { type ClosedCaptionsSettings, type StreamVideoParticipant, type StreamVi
|
|
|
5
5
|
import { CallStatsReport } from '../stats';
|
|
6
6
|
import { CallClosedCaption, CallIngressResponse, CallResponse, CallSessionResponse, CallSettingsResponse, EgressResponse, MemberResponse, OwnCapability, ThumbnailResponse, UserResponse, VideoEvent } from '../gen/coordinator';
|
|
7
7
|
import { ReconnectDetails } from '../gen/video/sfu/event/events';
|
|
8
|
-
import { CallState as SfuCallState, Pin, TrackType } from '../gen/video/sfu/models/models';
|
|
8
|
+
import { CallState as SfuCallState, Pin, TrackType, CallGrants } from '../gen/video/sfu/models/models';
|
|
9
9
|
import { Comparator } from '../sorting';
|
|
10
10
|
type OrphanedTrack = {
|
|
11
11
|
id: string;
|
|
@@ -45,6 +45,7 @@ export declare class CallState {
|
|
|
45
45
|
private callStatsReportSubject;
|
|
46
46
|
private closedCaptionsSubject;
|
|
47
47
|
private orphanedTracks;
|
|
48
|
+
private callGrantsSubject;
|
|
48
49
|
/**
|
|
49
50
|
* The time the call session actually started.
|
|
50
51
|
* Useful for displaying the call duration.
|
|
@@ -371,6 +372,13 @@ export declare class CallState {
|
|
|
371
372
|
* @param capabilities the capabilities to set.
|
|
372
373
|
*/
|
|
373
374
|
setOwnCapabilities: (capabilities: Patch<OwnCapability[]>) => OwnCapability[];
|
|
375
|
+
/**
|
|
376
|
+
* Sets the call grants (used for own capabilities).
|
|
377
|
+
*
|
|
378
|
+
* @internal
|
|
379
|
+
* @param grants the grants to set.
|
|
380
|
+
*/
|
|
381
|
+
setCallGrants: (grants: Patch<CallGrants>) => CallGrants;
|
|
374
382
|
/**
|
|
375
383
|
* The backstage state.
|
|
376
384
|
*/
|
package/package.json
CHANGED
package/src/Call.ts
CHANGED
|
@@ -112,6 +112,7 @@ import {
|
|
|
112
112
|
ClientCapability,
|
|
113
113
|
ClientDetails,
|
|
114
114
|
Codec,
|
|
115
|
+
ParticipantSource,
|
|
115
116
|
PublishOption,
|
|
116
117
|
SubscribeOption,
|
|
117
118
|
TrackType,
|
|
@@ -1015,6 +1016,7 @@ export class Call {
|
|
|
1015
1016
|
preferredPublishOptions,
|
|
1016
1017
|
preferredSubscribeOptions,
|
|
1017
1018
|
capabilities: Array.from(this.clientCapabilities),
|
|
1019
|
+
source: ParticipantSource.WEBRTC_UNSPECIFIED,
|
|
1018
1020
|
});
|
|
1019
1021
|
|
|
1020
1022
|
this.currentPublishOptions = publishOptions;
|
|
@@ -665,12 +665,19 @@ export class StreamClient {
|
|
|
665
665
|
};
|
|
666
666
|
}
|
|
667
667
|
|
|
668
|
+
const {
|
|
669
|
+
params: axiosConfigParams,
|
|
670
|
+
headers: axiosConfigHeaders,
|
|
671
|
+
...axiosRequestConfig
|
|
672
|
+
} = this.options.axiosRequestConfig || {};
|
|
673
|
+
|
|
668
674
|
return {
|
|
669
675
|
params: {
|
|
670
676
|
user_id: this.userID,
|
|
671
677
|
connection_id: this._getConnectionID(),
|
|
672
678
|
api_key: this.key,
|
|
673
679
|
...options.params,
|
|
680
|
+
...axiosConfigParams,
|
|
674
681
|
},
|
|
675
682
|
headers: {
|
|
676
683
|
...authorization,
|
|
@@ -680,9 +687,10 @@ export class StreamClient {
|
|
|
680
687
|
: this.getAuthType(),
|
|
681
688
|
'X-Stream-Client': this.getUserAgent(),
|
|
682
689
|
...options.headers,
|
|
690
|
+
...axiosConfigHeaders,
|
|
683
691
|
},
|
|
684
692
|
...options.config,
|
|
685
|
-
...
|
|
693
|
+
...axiosRequestConfig,
|
|
686
694
|
};
|
|
687
695
|
};
|
|
688
696
|
|
|
@@ -111,7 +111,14 @@ export abstract class InputMediaDeviceManager<
|
|
|
111
111
|
* Stops or pauses the stream based on state.disableMode
|
|
112
112
|
* @param {boolean} [forceStop=false] when true, stops the tracks regardless of the state.disableMode
|
|
113
113
|
*/
|
|
114
|
-
async disable(
|
|
114
|
+
async disable(options: { forceStop?: boolean }): Promise<void>;
|
|
115
|
+
async disable(forceStop?: boolean): Promise<void>;
|
|
116
|
+
async disable(forceStopOrOptions?: boolean | { forceStop?: boolean }) {
|
|
117
|
+
const forceStop =
|
|
118
|
+
typeof forceStopOrOptions === 'boolean'
|
|
119
|
+
? forceStopOrOptions
|
|
120
|
+
: (forceStopOrOptions?.forceStop ?? false);
|
|
121
|
+
|
|
115
122
|
this.state.prevStatus = this.state.optimisticStatus;
|
|
116
123
|
if (!forceStop && this.state.optimisticStatus === 'disabled') {
|
|
117
124
|
return;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CallState } from '../store';
|
|
2
2
|
import type { CallGrantsUpdated } from '../gen/video/sfu/event/events';
|
|
3
|
-
import { OwnCapability } from '../gen/coordinator';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Event handler that watches for `callGrantsUpdated` events.
|
|
@@ -10,26 +9,9 @@ import { OwnCapability } from '../gen/coordinator';
|
|
|
10
9
|
export const watchCallGrantsUpdated = (state: CallState) => {
|
|
11
10
|
return function onCallGrantsUpdated(event: CallGrantsUpdated) {
|
|
12
11
|
const { currentGrants } = event;
|
|
13
|
-
if (currentGrants) {
|
|
14
|
-
const { canPublishAudio, canPublishVideo, canScreenshare } =
|
|
15
|
-
currentGrants;
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
[OwnCapability.SEND_AUDIO]: canPublishAudio,
|
|
19
|
-
[OwnCapability.SEND_VIDEO]: canPublishVideo,
|
|
20
|
-
[OwnCapability.SCREENSHARE]: canScreenshare,
|
|
21
|
-
};
|
|
13
|
+
if (!currentGrants) return;
|
|
22
14
|
|
|
23
|
-
|
|
24
|
-
(capability) => update[capability] !== false,
|
|
25
|
-
);
|
|
26
|
-
Object.entries(update).forEach(([capability, value]) => {
|
|
27
|
-
if (value && !nextCapabilities.includes(capability as OwnCapability)) {
|
|
28
|
-
nextCapabilities.push(capability as OwnCapability);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
state.setOwnCapabilities(nextCapabilities);
|
|
33
|
-
}
|
|
15
|
+
state.setCallGrants(currentGrants);
|
|
34
16
|
};
|
|
35
17
|
};
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
ICETrickle as ICETrickle$,
|
|
16
16
|
Participant,
|
|
17
17
|
ParticipantCount,
|
|
18
|
+
ParticipantSource,
|
|
18
19
|
PeerType,
|
|
19
20
|
Pin,
|
|
20
21
|
PublishOption,
|
|
@@ -522,6 +523,10 @@ export interface JoinRequest {
|
|
|
522
523
|
* @generated from protobuf field: repeated stream.video.sfu.models.ClientCapability capabilities = 11;
|
|
523
524
|
*/
|
|
524
525
|
capabilities: ClientCapability[];
|
|
526
|
+
/**
|
|
527
|
+
* @generated from protobuf field: stream.video.sfu.models.ParticipantSource source = 12;
|
|
528
|
+
*/
|
|
529
|
+
source: ParticipantSource;
|
|
525
530
|
}
|
|
526
531
|
/**
|
|
527
532
|
* @generated from protobuf message stream.video.sfu.event.ReconnectDetails
|
|
@@ -1404,6 +1409,16 @@ class JoinRequest$Type extends MessageType<JoinRequest> {
|
|
|
1404
1409
|
'CLIENT_CAPABILITY_',
|
|
1405
1410
|
],
|
|
1406
1411
|
},
|
|
1412
|
+
{
|
|
1413
|
+
no: 12,
|
|
1414
|
+
name: 'source',
|
|
1415
|
+
kind: 'enum',
|
|
1416
|
+
T: () => [
|
|
1417
|
+
'stream.video.sfu.models.ParticipantSource',
|
|
1418
|
+
ParticipantSource,
|
|
1419
|
+
'PARTICIPANT_SOURCE_',
|
|
1420
|
+
],
|
|
1421
|
+
},
|
|
1407
1422
|
]);
|
|
1408
1423
|
}
|
|
1409
1424
|
}
|
|
@@ -137,6 +137,10 @@ export interface Participant {
|
|
|
137
137
|
* @generated from protobuf field: repeated string roles = 13;
|
|
138
138
|
*/
|
|
139
139
|
roles: string[];
|
|
140
|
+
/**
|
|
141
|
+
* @generated from protobuf field: stream.video.sfu.models.ParticipantSource source = 14;
|
|
142
|
+
*/
|
|
143
|
+
source: ParticipantSource;
|
|
140
144
|
}
|
|
141
145
|
/**
|
|
142
146
|
* @generated from protobuf message stream.video.sfu.models.StreamQuality
|
|
@@ -759,6 +763,37 @@ export enum TrackType {
|
|
|
759
763
|
*/
|
|
760
764
|
SCREEN_SHARE_AUDIO = 4,
|
|
761
765
|
}
|
|
766
|
+
/**
|
|
767
|
+
* must be aligned with kit
|
|
768
|
+
*
|
|
769
|
+
* @generated from protobuf enum stream.video.sfu.models.ParticipantSource
|
|
770
|
+
*/
|
|
771
|
+
export enum ParticipantSource {
|
|
772
|
+
/**
|
|
773
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_WEBRTC_UNSPECIFIED = 0;
|
|
774
|
+
*/
|
|
775
|
+
WEBRTC_UNSPECIFIED = 0,
|
|
776
|
+
/**
|
|
777
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_RTMP = 1;
|
|
778
|
+
*/
|
|
779
|
+
RTMP = 1,
|
|
780
|
+
/**
|
|
781
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_WHIP = 2;
|
|
782
|
+
*/
|
|
783
|
+
WHIP = 2,
|
|
784
|
+
/**
|
|
785
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_SIP = 3;
|
|
786
|
+
*/
|
|
787
|
+
SIP = 3,
|
|
788
|
+
/**
|
|
789
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_RTSP = 4;
|
|
790
|
+
*/
|
|
791
|
+
RTSP = 4,
|
|
792
|
+
/**
|
|
793
|
+
* @generated from protobuf enum value: PARTICIPANT_SOURCE_SRT = 5;
|
|
794
|
+
*/
|
|
795
|
+
SRT = 5,
|
|
796
|
+
}
|
|
762
797
|
/**
|
|
763
798
|
* @generated from protobuf enum stream.video.sfu.models.ErrorCode
|
|
764
799
|
*/
|
|
@@ -1211,6 +1246,16 @@ class Participant$Type extends MessageType<Participant> {
|
|
|
1211
1246
|
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
1212
1247
|
T: 9 /*ScalarType.STRING*/,
|
|
1213
1248
|
},
|
|
1249
|
+
{
|
|
1250
|
+
no: 14,
|
|
1251
|
+
name: 'source',
|
|
1252
|
+
kind: 'enum',
|
|
1253
|
+
T: () => [
|
|
1254
|
+
'stream.video.sfu.models.ParticipantSource',
|
|
1255
|
+
ParticipantSource,
|
|
1256
|
+
'PARTICIPANT_SOURCE_',
|
|
1257
|
+
],
|
|
1258
|
+
},
|
|
1214
1259
|
]);
|
|
1215
1260
|
}
|
|
1216
1261
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Comparator } from './';
|
|
2
2
|
import { StreamVideoParticipant } from '../types';
|
|
3
|
+
import { ParticipantSource } from '../gen/video/sfu/models/models';
|
|
3
4
|
import {
|
|
4
5
|
hasAudio,
|
|
5
6
|
hasScreenShare,
|
|
@@ -86,6 +87,20 @@ export const pinned: Comparator<StreamVideoParticipant> = (a, b) => {
|
|
|
86
87
|
return 0;
|
|
87
88
|
};
|
|
88
89
|
|
|
90
|
+
/**
|
|
91
|
+
* A comparator creator which will set up a comparator which prioritizes
|
|
92
|
+
* participants who are from a specific source (e.g., WebRTC, RTMP, WHIP...).
|
|
93
|
+
*
|
|
94
|
+
* @param source the source to prioritize.
|
|
95
|
+
*/
|
|
96
|
+
export const withParticipantSource =
|
|
97
|
+
(source: ParticipantSource): Comparator<StreamVideoParticipant> =>
|
|
98
|
+
(a, b) => {
|
|
99
|
+
if (a.source === source && b.source !== source) return -1;
|
|
100
|
+
if (a.source !== source && b.source === source) return 1;
|
|
101
|
+
return 0;
|
|
102
|
+
};
|
|
103
|
+
|
|
89
104
|
/**
|
|
90
105
|
* A comparator creator which will set up a comparator which prioritizes
|
|
91
106
|
* participants who have a specific reaction.
|
package/src/sorting/presets.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ParticipantSource } from '../gen/video/sfu/models/models';
|
|
1
2
|
import { StreamVideoParticipant, VisibilityState } from '../types';
|
|
2
3
|
import { combineComparators, conditional } from './comparator';
|
|
3
4
|
import {
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
role,
|
|
10
11
|
screenSharing,
|
|
11
12
|
speaking,
|
|
13
|
+
withParticipantSource,
|
|
12
14
|
} from './participants';
|
|
13
15
|
|
|
14
16
|
// a comparator decorator which applies the decorated comparator only if the
|
|
@@ -48,7 +50,6 @@ export const defaultSortPreset = combineComparators(
|
|
|
48
50
|
publishingAudio,
|
|
49
51
|
),
|
|
50
52
|
),
|
|
51
|
-
// ifInvisibleBy(name),
|
|
52
53
|
);
|
|
53
54
|
|
|
54
55
|
/**
|
|
@@ -66,7 +67,6 @@ export const speakerLayoutSortPreset = combineComparators(
|
|
|
66
67
|
publishingAudio,
|
|
67
68
|
),
|
|
68
69
|
),
|
|
69
|
-
// ifInvisibleBy(name),
|
|
70
70
|
);
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -84,7 +84,6 @@ export const paginatedLayoutSortPreset = combineComparators(
|
|
|
84
84
|
publishingAudio,
|
|
85
85
|
),
|
|
86
86
|
),
|
|
87
|
-
// ifInvisibleOrUnknownBy(name),
|
|
88
87
|
);
|
|
89
88
|
|
|
90
89
|
/**
|
|
@@ -96,10 +95,10 @@ export const livestreamOrAudioRoomSortPreset = combineComparators(
|
|
|
96
95
|
dominantSpeaker,
|
|
97
96
|
speaking,
|
|
98
97
|
reactionType('raised-hand'),
|
|
98
|
+
withParticipantSource(ParticipantSource.RTMP),
|
|
99
99
|
publishingVideo,
|
|
100
100
|
publishingAudio,
|
|
101
101
|
),
|
|
102
102
|
),
|
|
103
103
|
role('admin', 'host', 'speaker'),
|
|
104
|
-
// name,
|
|
105
104
|
);
|
package/src/store/CallState.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BehaviorSubject,
|
|
3
|
+
combineLatest,
|
|
3
4
|
distinctUntilChanged,
|
|
4
5
|
map,
|
|
5
6
|
Observable,
|
|
7
|
+
ReplaySubject,
|
|
6
8
|
shareReplay,
|
|
9
|
+
startWith,
|
|
7
10
|
} from 'rxjs';
|
|
8
11
|
import type { Patch } from './rxUtils';
|
|
9
12
|
import * as RxUtils from './rxUtils';
|
|
@@ -49,6 +52,7 @@ import {
|
|
|
49
52
|
CallState as SfuCallState,
|
|
50
53
|
Pin,
|
|
51
54
|
TrackType,
|
|
55
|
+
CallGrants,
|
|
52
56
|
} from '../gen/video/sfu/models/models';
|
|
53
57
|
import { Comparator, defaultSortPreset } from '../sorting';
|
|
54
58
|
import { getLogger } from '../logger';
|
|
@@ -128,6 +132,8 @@ export class CallState {
|
|
|
128
132
|
// We keep these tracks around until we can associate them with a participant.
|
|
129
133
|
private orphanedTracks: OrphanedTrack[] = [];
|
|
130
134
|
|
|
135
|
+
private callGrantsSubject = new ReplaySubject<CallGrants>(1);
|
|
136
|
+
|
|
131
137
|
// Derived state
|
|
132
138
|
|
|
133
139
|
/**
|
|
@@ -394,8 +400,12 @@ export class CallState {
|
|
|
394
400
|
*/
|
|
395
401
|
const isShallowEqual = <T>(a: Array<T>, b: Array<T>): boolean => {
|
|
396
402
|
if (a.length !== b.length) return false;
|
|
397
|
-
for (const item of a)
|
|
398
|
-
|
|
403
|
+
for (const item of a) {
|
|
404
|
+
if (!b.includes(item)) return false;
|
|
405
|
+
}
|
|
406
|
+
for (const item of b) {
|
|
407
|
+
if (!a.includes(item)) return false;
|
|
408
|
+
}
|
|
399
409
|
return true;
|
|
400
410
|
};
|
|
401
411
|
|
|
@@ -406,8 +416,7 @@ export class CallState {
|
|
|
406
416
|
const duc = <T>(
|
|
407
417
|
subject: BehaviorSubject<T>,
|
|
408
418
|
comparator?: (a: T, b: T) => boolean,
|
|
409
|
-
): Observable<T> =>
|
|
410
|
-
subject.asObservable().pipe(distinctUntilChanged(comparator));
|
|
419
|
+
): Observable<T> => subject.pipe(distinctUntilChanged(comparator));
|
|
411
420
|
|
|
412
421
|
// primitive values should only emit once the value they hold changes
|
|
413
422
|
this.anonymousParticipantCount$ = duc(
|
|
@@ -416,7 +425,41 @@ export class CallState {
|
|
|
416
425
|
this.blockedUserIds$ = duc(this.blockedUserIdsSubject, isShallowEqual);
|
|
417
426
|
this.backstage$ = duc(this.backstageSubject);
|
|
418
427
|
this.callingState$ = duc(this.callingStateSubject);
|
|
419
|
-
this.ownCapabilities$ =
|
|
428
|
+
this.ownCapabilities$ = combineLatest([
|
|
429
|
+
this.ownCapabilitiesSubject,
|
|
430
|
+
this.callGrantsSubject.pipe(startWith(undefined)),
|
|
431
|
+
]).pipe(
|
|
432
|
+
map(([capabilities, grants]) => {
|
|
433
|
+
if (!grants) return capabilities;
|
|
434
|
+
|
|
435
|
+
const { canPublishAudio, canPublishVideo, canScreenshare } = grants;
|
|
436
|
+
|
|
437
|
+
const update = {
|
|
438
|
+
[OwnCapability.SEND_AUDIO]: canPublishAudio,
|
|
439
|
+
[OwnCapability.SEND_VIDEO]: canPublishVideo,
|
|
440
|
+
[OwnCapability.SCREENSHARE]: canScreenshare,
|
|
441
|
+
} as const;
|
|
442
|
+
|
|
443
|
+
const nextCapabilities = [...capabilities];
|
|
444
|
+
|
|
445
|
+
for (const _capability in update) {
|
|
446
|
+
const capability = _capability as keyof typeof update;
|
|
447
|
+
const allowed = update[capability];
|
|
448
|
+
|
|
449
|
+
// grants take precedence over capabilities, reconstruct the capabilities
|
|
450
|
+
if (allowed && !nextCapabilities.includes(capability)) {
|
|
451
|
+
nextCapabilities.push(capability);
|
|
452
|
+
} else if (!allowed && nextCapabilities.includes(capability)) {
|
|
453
|
+
const index = nextCapabilities.indexOf(capability);
|
|
454
|
+
nextCapabilities.splice(index, 1);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return nextCapabilities;
|
|
459
|
+
}),
|
|
460
|
+
distinctUntilChanged(isShallowEqual),
|
|
461
|
+
shareReplay({ bufferSize: 1, refCount: true }),
|
|
462
|
+
);
|
|
420
463
|
this.participantCount$ = duc(this.participantCountSubject);
|
|
421
464
|
this.recording$ = duc(this.recordingSubject);
|
|
422
465
|
this.transcribing$ = duc(this.transcribingSubject);
|
|
@@ -767,6 +810,16 @@ export class CallState {
|
|
|
767
810
|
return this.setCurrentValue(this.ownCapabilitiesSubject, capabilities);
|
|
768
811
|
};
|
|
769
812
|
|
|
813
|
+
/**
|
|
814
|
+
* Sets the call grants (used for own capabilities).
|
|
815
|
+
*
|
|
816
|
+
* @internal
|
|
817
|
+
* @param grants the grants to set.
|
|
818
|
+
*/
|
|
819
|
+
setCallGrants = (grants: Patch<CallGrants>) => {
|
|
820
|
+
return this.setCurrentValue(this.callGrantsSubject, grants);
|
|
821
|
+
};
|
|
822
|
+
|
|
770
823
|
/**
|
|
771
824
|
* The backstage state.
|
|
772
825
|
*/
|
|
@@ -1412,7 +1465,7 @@ export class CallState {
|
|
|
1412
1465
|
|
|
1413
1466
|
private updateOwnCapabilities = (event: UpdatedCallPermissionsEvent) => {
|
|
1414
1467
|
if (event.user.id === this.localParticipant?.userId) {
|
|
1415
|
-
this.
|
|
1468
|
+
this.setOwnCapabilities(event.own_capabilities);
|
|
1416
1469
|
}
|
|
1417
1470
|
};
|
|
1418
1471
|
|