@stream-io/video-client 1.14.0 → 1.15.1
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 +1533 -1783
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +1514 -1783
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +1533 -1783
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +43 -28
- package/dist/src/StreamSfuClient.d.ts +4 -5
- package/dist/src/devices/CameraManager.d.ts +5 -8
- package/dist/src/devices/InputMediaDeviceManager.d.ts +5 -5
- package/dist/src/devices/MicrophoneManager.d.ts +7 -2
- package/dist/src/devices/ScreenShareManager.d.ts +1 -2
- package/dist/src/gen/video/sfu/event/events.d.ts +38 -19
- package/dist/src/gen/video/sfu/models/models.d.ts +76 -9
- package/dist/src/helpers/array.d.ts +7 -0
- package/dist/src/permissions/PermissionsContext.d.ts +6 -0
- package/dist/src/rtc/BasePeerConnection.d.ts +90 -0
- package/dist/src/rtc/Dispatcher.d.ts +0 -1
- package/dist/src/rtc/IceTrickleBuffer.d.ts +3 -2
- package/dist/src/rtc/Publisher.d.ts +32 -86
- package/dist/src/rtc/Subscriber.d.ts +4 -56
- package/dist/src/rtc/TransceiverCache.d.ts +55 -0
- package/dist/src/rtc/codecs.d.ts +1 -15
- package/dist/src/rtc/helpers/sdp.d.ts +8 -0
- package/dist/src/rtc/helpers/tracks.d.ts +1 -0
- package/dist/src/rtc/index.d.ts +3 -0
- package/dist/src/rtc/videoLayers.d.ts +11 -25
- package/dist/src/stats/{stateStoreStatsReporter.d.ts → CallStateStatsReporter.d.ts} +5 -1
- package/dist/src/stats/SfuStatsReporter.d.ts +4 -2
- package/dist/src/stats/index.d.ts +1 -1
- package/dist/src/stats/types.d.ts +8 -0
- package/dist/src/types.d.ts +12 -22
- package/package.json +1 -1
- package/src/Call.ts +254 -268
- package/src/StreamSfuClient.ts +9 -14
- package/src/StreamVideoClient.ts +1 -1
- package/src/__tests__/Call.publishing.test.ts +306 -0
- package/src/devices/CameraManager.ts +33 -16
- package/src/devices/InputMediaDeviceManager.ts +38 -27
- package/src/devices/MicrophoneManager.ts +29 -8
- package/src/devices/ScreenShareManager.ts +6 -8
- package/src/devices/__tests__/CameraManager.test.ts +111 -14
- package/src/devices/__tests__/InputMediaDeviceManager.test.ts +4 -4
- package/src/devices/__tests__/MicrophoneManager.test.ts +59 -21
- package/src/devices/__tests__/ScreenShareManager.test.ts +5 -5
- package/src/devices/__tests__/mocks.ts +1 -0
- package/src/events/__tests__/internal.test.ts +132 -0
- package/src/events/__tests__/mutes.test.ts +0 -3
- package/src/events/__tests__/speaker.test.ts +92 -0
- package/src/events/participant.ts +3 -4
- package/src/gen/video/sfu/event/events.ts +91 -30
- package/src/gen/video/sfu/models/models.ts +105 -13
- package/src/helpers/array.ts +14 -0
- package/src/permissions/PermissionsContext.ts +22 -0
- package/src/permissions/__tests__/PermissionsContext.test.ts +40 -0
- package/src/rpc/__tests__/createClient.test.ts +38 -0
- package/src/rpc/createClient.ts +11 -5
- package/src/rtc/BasePeerConnection.ts +240 -0
- package/src/rtc/Dispatcher.ts +0 -9
- package/src/rtc/IceTrickleBuffer.ts +24 -4
- package/src/rtc/Publisher.ts +210 -528
- package/src/rtc/Subscriber.ts +26 -200
- package/src/rtc/TransceiverCache.ts +120 -0
- package/src/rtc/__tests__/Publisher.test.ts +407 -210
- package/src/rtc/__tests__/Subscriber.test.ts +88 -36
- package/src/rtc/__tests__/mocks/webrtc.mocks.ts +22 -2
- package/src/rtc/__tests__/videoLayers.test.ts +161 -54
- package/src/rtc/codecs.ts +1 -131
- package/src/rtc/helpers/__tests__/rtcConfiguration.test.ts +34 -0
- package/src/rtc/helpers/__tests__/sdp.test.ts +59 -0
- package/src/rtc/helpers/sdp.ts +30 -0
- package/src/rtc/helpers/tracks.ts +3 -0
- package/src/rtc/index.ts +4 -0
- package/src/rtc/videoLayers.ts +68 -76
- package/src/stats/{stateStoreStatsReporter.ts → CallStateStatsReporter.ts} +58 -27
- package/src/stats/SfuStatsReporter.ts +31 -3
- package/src/stats/index.ts +1 -1
- package/src/stats/types.ts +12 -0
- package/src/types.ts +12 -22
- package/dist/src/helpers/sdp-munging.d.ts +0 -24
- package/dist/src/rtc/bitrateLookup.d.ts +0 -2
- package/dist/src/rtc/helpers/iceCandidate.d.ts +0 -2
- package/src/helpers/__tests__/hq-audio-sdp.ts +0 -332
- package/src/helpers/__tests__/sdp-munging.test.ts +0 -283
- package/src/helpers/sdp-munging.ts +0 -265
- package/src/rtc/__tests__/bitrateLookup.test.ts +0 -12
- package/src/rtc/__tests__/codecs.test.ts +0 -145
- package/src/rtc/bitrateLookup.ts +0 -61
- package/src/rtc/helpers/iceCandidate.ts +0 -16
- /package/dist/src/{compatibility.d.ts → helpers/compatibility.d.ts} +0 -0
- /package/src/{compatibility.ts → helpers/compatibility.ts} +0 -0
|
@@ -8,10 +8,10 @@ import { GoAwayReason } from '../models/models';
|
|
|
8
8
|
import { CallGrants } from '../models/models';
|
|
9
9
|
import { Codec } from '../models/models';
|
|
10
10
|
import { ConnectionQuality } from '../models/models';
|
|
11
|
-
import { PublishOptions } from '../models/models';
|
|
12
11
|
import { CallState } from '../models/models';
|
|
13
12
|
import { TrackSubscriptionDetails } from '../signal_rpc/signal';
|
|
14
13
|
import { TrackInfo } from '../models/models';
|
|
14
|
+
import { SubscribeOption } from '../models/models';
|
|
15
15
|
import { ClientDetails } from '../models/models';
|
|
16
16
|
import { TrackUnpublishReason } from '../models/models';
|
|
17
17
|
import { Participant } from '../models/models';
|
|
@@ -244,16 +244,6 @@ export interface SfuEvent {
|
|
|
244
244
|
*/
|
|
245
245
|
participantMigrationComplete: ParticipantMigrationComplete;
|
|
246
246
|
}
|
|
247
|
-
| {
|
|
248
|
-
oneofKind: 'codecNegotiationComplete';
|
|
249
|
-
/**
|
|
250
|
-
* CodecNegotiationComplete is sent to signal the completion of a codec negotiation.
|
|
251
|
-
* SDKs can safely stop previous transceivers
|
|
252
|
-
*
|
|
253
|
-
* @generated from protobuf field: stream.video.sfu.event.CodecNegotiationComplete codec_negotiation_complete = 26;
|
|
254
|
-
*/
|
|
255
|
-
codecNegotiationComplete: CodecNegotiationComplete;
|
|
256
|
-
}
|
|
257
247
|
| {
|
|
258
248
|
oneofKind: 'changePublishOptions';
|
|
259
249
|
/**
|
|
@@ -272,14 +262,18 @@ export interface SfuEvent {
|
|
|
272
262
|
*/
|
|
273
263
|
export interface ChangePublishOptions {
|
|
274
264
|
/**
|
|
275
|
-
* @generated from protobuf field: stream.video.sfu.models.PublishOption
|
|
265
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.PublishOption publish_options = 1;
|
|
276
266
|
*/
|
|
277
|
-
|
|
267
|
+
publishOptions: PublishOption[];
|
|
268
|
+
/**
|
|
269
|
+
* @generated from protobuf field: string reason = 2;
|
|
270
|
+
*/
|
|
271
|
+
reason: string;
|
|
278
272
|
}
|
|
279
273
|
/**
|
|
280
|
-
* @generated from protobuf message stream.video.sfu.event.
|
|
274
|
+
* @generated from protobuf message stream.video.sfu.event.ChangePublishOptionsComplete
|
|
281
275
|
*/
|
|
282
|
-
export interface
|
|
276
|
+
export interface ChangePublishOptionsComplete {}
|
|
283
277
|
/**
|
|
284
278
|
* @generated from protobuf message stream.video.sfu.event.ParticipantMigrationComplete
|
|
285
279
|
*/
|
|
@@ -504,6 +498,14 @@ export interface JoinRequest {
|
|
|
504
498
|
* @generated from protobuf field: stream.video.sfu.event.ReconnectDetails reconnect_details = 7;
|
|
505
499
|
*/
|
|
506
500
|
reconnectDetails?: ReconnectDetails;
|
|
501
|
+
/**
|
|
502
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.PublishOption preferred_publish_options = 9;
|
|
503
|
+
*/
|
|
504
|
+
preferredPublishOptions: PublishOption[];
|
|
505
|
+
/**
|
|
506
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.SubscribeOption preferred_subscribe_options = 10;
|
|
507
|
+
*/
|
|
508
|
+
preferredSubscribeOptions: SubscribeOption[];
|
|
507
509
|
}
|
|
508
510
|
/**
|
|
509
511
|
* @generated from protobuf message stream.video.sfu.event.ReconnectDetails
|
|
@@ -570,9 +572,9 @@ export interface JoinResponse {
|
|
|
570
572
|
*/
|
|
571
573
|
fastReconnectDeadlineSeconds: number;
|
|
572
574
|
/**
|
|
573
|
-
* @generated from protobuf field: stream.video.sfu.models.
|
|
575
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.PublishOption publish_options = 4;
|
|
574
576
|
*/
|
|
575
|
-
publishOptions
|
|
577
|
+
publishOptions: PublishOption[];
|
|
576
578
|
}
|
|
577
579
|
/**
|
|
578
580
|
* ParticipantJoined is fired when a user joins a call
|
|
@@ -729,6 +731,14 @@ export interface AudioSender {
|
|
|
729
731
|
* @generated from protobuf field: stream.video.sfu.models.Codec codec = 2;
|
|
730
732
|
*/
|
|
731
733
|
codec?: Codec;
|
|
734
|
+
/**
|
|
735
|
+
* @generated from protobuf field: stream.video.sfu.models.TrackType track_type = 3;
|
|
736
|
+
*/
|
|
737
|
+
trackType: TrackType;
|
|
738
|
+
/**
|
|
739
|
+
* @generated from protobuf field: int32 publish_option_id = 4;
|
|
740
|
+
*/
|
|
741
|
+
publishOptionId: number;
|
|
732
742
|
}
|
|
733
743
|
/**
|
|
734
744
|
* VideoLayerSetting is used to specify various parameters of a particular encoding in simulcast.
|
|
@@ -779,6 +789,14 @@ export interface VideoSender {
|
|
|
779
789
|
* @generated from protobuf field: repeated stream.video.sfu.event.VideoLayerSetting layers = 3;
|
|
780
790
|
*/
|
|
781
791
|
layers: VideoLayerSetting[];
|
|
792
|
+
/**
|
|
793
|
+
* @generated from protobuf field: stream.video.sfu.models.TrackType track_type = 4;
|
|
794
|
+
*/
|
|
795
|
+
trackType: TrackType;
|
|
796
|
+
/**
|
|
797
|
+
* @generated from protobuf field: int32 publish_option_id = 5;
|
|
798
|
+
*/
|
|
799
|
+
publishOptionId: number;
|
|
782
800
|
}
|
|
783
801
|
/**
|
|
784
802
|
* sent to users when they need to change the quality of their video
|
|
@@ -1000,13 +1018,6 @@ class SfuEvent$Type extends MessageType<SfuEvent> {
|
|
|
1000
1018
|
oneof: 'eventPayload',
|
|
1001
1019
|
T: () => ParticipantMigrationComplete,
|
|
1002
1020
|
},
|
|
1003
|
-
{
|
|
1004
|
-
no: 26,
|
|
1005
|
-
name: 'codec_negotiation_complete',
|
|
1006
|
-
kind: 'message',
|
|
1007
|
-
oneof: 'eventPayload',
|
|
1008
|
-
T: () => CodecNegotiationComplete,
|
|
1009
|
-
},
|
|
1010
1021
|
{
|
|
1011
1022
|
no: 27,
|
|
1012
1023
|
name: 'change_publish_options',
|
|
@@ -1027,10 +1038,12 @@ class ChangePublishOptions$Type extends MessageType<ChangePublishOptions> {
|
|
|
1027
1038
|
super('stream.video.sfu.event.ChangePublishOptions', [
|
|
1028
1039
|
{
|
|
1029
1040
|
no: 1,
|
|
1030
|
-
name: '
|
|
1041
|
+
name: 'publish_options',
|
|
1031
1042
|
kind: 'message',
|
|
1043
|
+
repeat: 1 /*RepeatType.PACKED*/,
|
|
1032
1044
|
T: () => PublishOption,
|
|
1033
1045
|
},
|
|
1046
|
+
{ no: 2, name: 'reason', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
1034
1047
|
]);
|
|
1035
1048
|
}
|
|
1036
1049
|
}
|
|
@@ -1039,15 +1052,16 @@ class ChangePublishOptions$Type extends MessageType<ChangePublishOptions> {
|
|
|
1039
1052
|
*/
|
|
1040
1053
|
export const ChangePublishOptions = new ChangePublishOptions$Type();
|
|
1041
1054
|
// @generated message type with reflection information, may provide speed optimized methods
|
|
1042
|
-
class
|
|
1055
|
+
class ChangePublishOptionsComplete$Type extends MessageType<ChangePublishOptionsComplete> {
|
|
1043
1056
|
constructor() {
|
|
1044
|
-
super('stream.video.sfu.event.
|
|
1057
|
+
super('stream.video.sfu.event.ChangePublishOptionsComplete', []);
|
|
1045
1058
|
}
|
|
1046
1059
|
}
|
|
1047
1060
|
/**
|
|
1048
|
-
* @generated MessageType for protobuf message stream.video.sfu.event.
|
|
1061
|
+
* @generated MessageType for protobuf message stream.video.sfu.event.ChangePublishOptionsComplete
|
|
1049
1062
|
*/
|
|
1050
|
-
export const
|
|
1063
|
+
export const ChangePublishOptionsComplete =
|
|
1064
|
+
new ChangePublishOptionsComplete$Type();
|
|
1051
1065
|
// @generated message type with reflection information, may provide speed optimized methods
|
|
1052
1066
|
class ParticipantMigrationComplete$Type extends MessageType<ParticipantMigrationComplete> {
|
|
1053
1067
|
constructor() {
|
|
@@ -1306,6 +1320,20 @@ class JoinRequest$Type extends MessageType<JoinRequest> {
|
|
|
1306
1320
|
kind: 'message',
|
|
1307
1321
|
T: () => ReconnectDetails,
|
|
1308
1322
|
},
|
|
1323
|
+
{
|
|
1324
|
+
no: 9,
|
|
1325
|
+
name: 'preferred_publish_options',
|
|
1326
|
+
kind: 'message',
|
|
1327
|
+
repeat: 1 /*RepeatType.PACKED*/,
|
|
1328
|
+
T: () => PublishOption,
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
no: 10,
|
|
1332
|
+
name: 'preferred_subscribe_options',
|
|
1333
|
+
kind: 'message',
|
|
1334
|
+
repeat: 1 /*RepeatType.PACKED*/,
|
|
1335
|
+
T: () => SubscribeOption,
|
|
1336
|
+
},
|
|
1309
1337
|
]);
|
|
1310
1338
|
}
|
|
1311
1339
|
}
|
|
@@ -1413,7 +1441,8 @@ class JoinResponse$Type extends MessageType<JoinResponse> {
|
|
|
1413
1441
|
no: 4,
|
|
1414
1442
|
name: 'publish_options',
|
|
1415
1443
|
kind: 'message',
|
|
1416
|
-
|
|
1444
|
+
repeat: 1 /*RepeatType.PACKED*/,
|
|
1445
|
+
T: () => PublishOption,
|
|
1417
1446
|
},
|
|
1418
1447
|
]);
|
|
1419
1448
|
}
|
|
@@ -1578,6 +1607,22 @@ class AudioSender$Type extends MessageType<AudioSender> {
|
|
|
1578
1607
|
constructor() {
|
|
1579
1608
|
super('stream.video.sfu.event.AudioSender', [
|
|
1580
1609
|
{ no: 2, name: 'codec', kind: 'message', T: () => Codec },
|
|
1610
|
+
{
|
|
1611
|
+
no: 3,
|
|
1612
|
+
name: 'track_type',
|
|
1613
|
+
kind: 'enum',
|
|
1614
|
+
T: () => [
|
|
1615
|
+
'stream.video.sfu.models.TrackType',
|
|
1616
|
+
TrackType,
|
|
1617
|
+
'TRACK_TYPE_',
|
|
1618
|
+
],
|
|
1619
|
+
},
|
|
1620
|
+
{
|
|
1621
|
+
no: 4,
|
|
1622
|
+
name: 'publish_option_id',
|
|
1623
|
+
kind: 'scalar',
|
|
1624
|
+
T: 5 /*ScalarType.INT32*/,
|
|
1625
|
+
},
|
|
1581
1626
|
]);
|
|
1582
1627
|
}
|
|
1583
1628
|
}
|
|
@@ -1630,6 +1675,22 @@ class VideoSender$Type extends MessageType<VideoSender> {
|
|
|
1630
1675
|
repeat: 1 /*RepeatType.PACKED*/,
|
|
1631
1676
|
T: () => VideoLayerSetting,
|
|
1632
1677
|
},
|
|
1678
|
+
{
|
|
1679
|
+
no: 4,
|
|
1680
|
+
name: 'track_type',
|
|
1681
|
+
kind: 'enum',
|
|
1682
|
+
T: () => [
|
|
1683
|
+
'stream.video.sfu.models.TrackType',
|
|
1684
|
+
TrackType,
|
|
1685
|
+
'TRACK_TYPE_',
|
|
1686
|
+
],
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
no: 5,
|
|
1690
|
+
name: 'publish_option_id',
|
|
1691
|
+
kind: 'scalar',
|
|
1692
|
+
T: 5 /*ScalarType.INT32*/,
|
|
1693
|
+
},
|
|
1633
1694
|
]);
|
|
1634
1695
|
}
|
|
1635
1696
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { MessageType } from '@protobuf-ts/runtime';
|
|
6
6
|
import { Struct } from '../../../google/protobuf/struct';
|
|
7
7
|
import { Timestamp } from '../../../google/protobuf/timestamp';
|
|
8
|
+
|
|
8
9
|
/**
|
|
9
10
|
* CallState is the current state of the call
|
|
10
11
|
* as seen by an SFU.
|
|
@@ -194,49 +195,108 @@ export interface VideoLayer {
|
|
|
194
195
|
quality: VideoQuality;
|
|
195
196
|
}
|
|
196
197
|
/**
|
|
197
|
-
*
|
|
198
|
+
* SubscribeOption represents the configuration options for subscribing to a track.
|
|
199
|
+
*
|
|
200
|
+
* @generated from protobuf message stream.video.sfu.models.SubscribeOption
|
|
198
201
|
*/
|
|
199
|
-
export interface
|
|
202
|
+
export interface SubscribeOption {
|
|
203
|
+
/**
|
|
204
|
+
* The type of the track being subscribed (e.g., video, screenshare).
|
|
205
|
+
*
|
|
206
|
+
* @generated from protobuf field: stream.video.sfu.models.TrackType track_type = 1;
|
|
207
|
+
*/
|
|
208
|
+
trackType: TrackType;
|
|
200
209
|
/**
|
|
201
|
-
*
|
|
210
|
+
* The codecs supported by the subscriber for decoding tracks.
|
|
211
|
+
*
|
|
212
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.Codec codecs = 2;
|
|
202
213
|
*/
|
|
203
|
-
codecs:
|
|
214
|
+
codecs: Codec[];
|
|
204
215
|
}
|
|
205
216
|
/**
|
|
217
|
+
* PublishOption represents the configuration options for publishing a track.
|
|
218
|
+
*
|
|
206
219
|
* @generated from protobuf message stream.video.sfu.models.PublishOption
|
|
207
220
|
*/
|
|
208
221
|
export interface PublishOption {
|
|
209
222
|
/**
|
|
223
|
+
* The type of the track being published (e.g., video, screenshare).
|
|
224
|
+
*
|
|
210
225
|
* @generated from protobuf field: stream.video.sfu.models.TrackType track_type = 1;
|
|
211
226
|
*/
|
|
212
227
|
trackType: TrackType;
|
|
213
228
|
/**
|
|
229
|
+
* The codec to be used for encoding the track (e.g., VP8, VP9, H264).
|
|
230
|
+
*
|
|
214
231
|
* @generated from protobuf field: stream.video.sfu.models.Codec codec = 2;
|
|
215
232
|
*/
|
|
216
233
|
codec?: Codec;
|
|
217
234
|
/**
|
|
235
|
+
* The target bitrate for the published track, in bits per second.
|
|
236
|
+
*
|
|
218
237
|
* @generated from protobuf field: int32 bitrate = 3;
|
|
219
238
|
*/
|
|
220
239
|
bitrate: number;
|
|
221
240
|
/**
|
|
241
|
+
* The target frames per second (FPS) for video encoding.
|
|
242
|
+
*
|
|
222
243
|
* @generated from protobuf field: int32 fps = 4;
|
|
223
244
|
*/
|
|
224
245
|
fps: number;
|
|
225
246
|
/**
|
|
247
|
+
* The maximum number of spatial layers to send.
|
|
248
|
+
* - For SVC (e.g., VP9), spatial layers downscale by a factor of 2:
|
|
249
|
+
* - 1 layer: full resolution
|
|
250
|
+
* - 2 layers: full resolution + half resolution
|
|
251
|
+
* - 3 layers: full resolution + half resolution + quarter resolution
|
|
252
|
+
* - For non-SVC codecs (e.g., VP8/H264), this determines the number of
|
|
253
|
+
* encoded resolutions (e.g., quarter, half, full) sent for simulcast.
|
|
254
|
+
*
|
|
226
255
|
* @generated from protobuf field: int32 max_spatial_layers = 5;
|
|
227
256
|
*/
|
|
228
257
|
maxSpatialLayers: number;
|
|
229
258
|
/**
|
|
259
|
+
* The maximum number of temporal layers for scalable video coding (SVC).
|
|
260
|
+
* Temporal layers allow varying frame rates for different bandwidths.
|
|
261
|
+
*
|
|
230
262
|
* @generated from protobuf field: int32 max_temporal_layers = 6;
|
|
231
263
|
*/
|
|
232
264
|
maxTemporalLayers: number;
|
|
265
|
+
/**
|
|
266
|
+
* The dimensions of the video (e.g., width and height in pixels).
|
|
267
|
+
* Spatial layers are based on this base resolution. For example, if the base
|
|
268
|
+
* resolution is 1280x720:
|
|
269
|
+
* - Full resolution (1 layer) = 1280x720
|
|
270
|
+
* - Half resolution (2 layers) = 640x360
|
|
271
|
+
* - Quarter resolution (3 layers) = 320x180
|
|
272
|
+
*
|
|
273
|
+
* @generated from protobuf field: stream.video.sfu.models.VideoDimension video_dimension = 7;
|
|
274
|
+
*/
|
|
275
|
+
videoDimension?: VideoDimension;
|
|
276
|
+
/**
|
|
277
|
+
* The unique identifier for the publish request.
|
|
278
|
+
* - This `id` is assigned exclusively by the SFU. Any `id` set by the client
|
|
279
|
+
* in the `PublishOption` will be ignored and overwritten by the SFU.
|
|
280
|
+
* - The primary purpose of this `id` is to uniquely identify each publish
|
|
281
|
+
* request, even in scenarios where multiple publish requests for the same
|
|
282
|
+
* `track_type` and `codec` are active simultaneously.
|
|
283
|
+
* For example:
|
|
284
|
+
* - A user may publish two tracks of the same type (e.g., video) and codec
|
|
285
|
+
* (e.g., VP9) concurrently.
|
|
286
|
+
* - This uniqueness ensures that individual requests can be managed
|
|
287
|
+
* independently. For instance, an `id` is critical when stopping a specific
|
|
288
|
+
* publish request without affecting others.
|
|
289
|
+
*
|
|
290
|
+
* @generated from protobuf field: int32 id = 8;
|
|
291
|
+
*/
|
|
292
|
+
id: number;
|
|
233
293
|
}
|
|
234
294
|
/**
|
|
235
295
|
* @generated from protobuf message stream.video.sfu.models.Codec
|
|
236
296
|
*/
|
|
237
297
|
export interface Codec {
|
|
238
298
|
/**
|
|
239
|
-
* @generated from protobuf field: uint32 payload_type =
|
|
299
|
+
* @generated from protobuf field: uint32 payload_type = 16;
|
|
240
300
|
*/
|
|
241
301
|
payloadType: number;
|
|
242
302
|
/**
|
|
@@ -248,7 +308,7 @@ export interface Codec {
|
|
|
248
308
|
*/
|
|
249
309
|
clockRate: number;
|
|
250
310
|
/**
|
|
251
|
-
* @generated from protobuf field: string encoding_parameters =
|
|
311
|
+
* @generated from protobuf field: string encoding_parameters = 15;
|
|
252
312
|
*/
|
|
253
313
|
encodingParameters: string;
|
|
254
314
|
/**
|
|
@@ -311,6 +371,14 @@ export interface TrackInfo {
|
|
|
311
371
|
* @generated from protobuf field: bool muted = 10;
|
|
312
372
|
*/
|
|
313
373
|
muted: boolean;
|
|
374
|
+
/**
|
|
375
|
+
* @generated from protobuf field: stream.video.sfu.models.Codec codec = 11;
|
|
376
|
+
*/
|
|
377
|
+
codec?: Codec;
|
|
378
|
+
/**
|
|
379
|
+
* @generated from protobuf field: int32 publish_option_id = 12;
|
|
380
|
+
*/
|
|
381
|
+
publishOptionId: number;
|
|
314
382
|
}
|
|
315
383
|
/**
|
|
316
384
|
* @generated from protobuf message stream.video.sfu.models.Error
|
|
@@ -1102,23 +1170,33 @@ class VideoLayer$Type extends MessageType<VideoLayer> {
|
|
|
1102
1170
|
*/
|
|
1103
1171
|
export const VideoLayer = new VideoLayer$Type();
|
|
1104
1172
|
// @generated message type with reflection information, may provide speed optimized methods
|
|
1105
|
-
class
|
|
1173
|
+
class SubscribeOption$Type extends MessageType<SubscribeOption> {
|
|
1106
1174
|
constructor() {
|
|
1107
|
-
super('stream.video.sfu.models.
|
|
1175
|
+
super('stream.video.sfu.models.SubscribeOption', [
|
|
1108
1176
|
{
|
|
1109
1177
|
no: 1,
|
|
1178
|
+
name: 'track_type',
|
|
1179
|
+
kind: 'enum',
|
|
1180
|
+
T: () => [
|
|
1181
|
+
'stream.video.sfu.models.TrackType',
|
|
1182
|
+
TrackType,
|
|
1183
|
+
'TRACK_TYPE_',
|
|
1184
|
+
],
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
no: 2,
|
|
1110
1188
|
name: 'codecs',
|
|
1111
1189
|
kind: 'message',
|
|
1112
1190
|
repeat: 1 /*RepeatType.PACKED*/,
|
|
1113
|
-
T: () =>
|
|
1191
|
+
T: () => Codec,
|
|
1114
1192
|
},
|
|
1115
1193
|
]);
|
|
1116
1194
|
}
|
|
1117
1195
|
}
|
|
1118
1196
|
/**
|
|
1119
|
-
* @generated MessageType for protobuf message stream.video.sfu.models.
|
|
1197
|
+
* @generated MessageType for protobuf message stream.video.sfu.models.SubscribeOption
|
|
1120
1198
|
*/
|
|
1121
|
-
export const
|
|
1199
|
+
export const SubscribeOption = new SubscribeOption$Type();
|
|
1122
1200
|
// @generated message type with reflection information, may provide speed optimized methods
|
|
1123
1201
|
class PublishOption$Type extends MessageType<PublishOption> {
|
|
1124
1202
|
constructor() {
|
|
@@ -1148,6 +1226,13 @@ class PublishOption$Type extends MessageType<PublishOption> {
|
|
|
1148
1226
|
kind: 'scalar',
|
|
1149
1227
|
T: 5 /*ScalarType.INT32*/,
|
|
1150
1228
|
},
|
|
1229
|
+
{
|
|
1230
|
+
no: 7,
|
|
1231
|
+
name: 'video_dimension',
|
|
1232
|
+
kind: 'message',
|
|
1233
|
+
T: () => VideoDimension,
|
|
1234
|
+
},
|
|
1235
|
+
{ no: 8, name: 'id', kind: 'scalar', T: 5 /*ScalarType.INT32*/ },
|
|
1151
1236
|
]);
|
|
1152
1237
|
}
|
|
1153
1238
|
}
|
|
@@ -1160,7 +1245,7 @@ class Codec$Type extends MessageType<Codec> {
|
|
|
1160
1245
|
constructor() {
|
|
1161
1246
|
super('stream.video.sfu.models.Codec', [
|
|
1162
1247
|
{
|
|
1163
|
-
no:
|
|
1248
|
+
no: 16,
|
|
1164
1249
|
name: 'payload_type',
|
|
1165
1250
|
kind: 'scalar',
|
|
1166
1251
|
T: 13 /*ScalarType.UINT32*/,
|
|
@@ -1173,7 +1258,7 @@ class Codec$Type extends MessageType<Codec> {
|
|
|
1173
1258
|
T: 13 /*ScalarType.UINT32*/,
|
|
1174
1259
|
},
|
|
1175
1260
|
{
|
|
1176
|
-
no:
|
|
1261
|
+
no: 15,
|
|
1177
1262
|
name: 'encoding_parameters',
|
|
1178
1263
|
kind: 'scalar',
|
|
1179
1264
|
T: 9 /*ScalarType.STRING*/,
|
|
@@ -1237,6 +1322,13 @@ class TrackInfo$Type extends MessageType<TrackInfo> {
|
|
|
1237
1322
|
{ no: 8, name: 'stereo', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
|
|
1238
1323
|
{ no: 9, name: 'red', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
|
|
1239
1324
|
{ no: 10, name: 'muted', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
|
|
1325
|
+
{ no: 11, name: 'codec', kind: 'message', T: () => Codec },
|
|
1326
|
+
{
|
|
1327
|
+
no: 12,
|
|
1328
|
+
name: 'publish_option_id',
|
|
1329
|
+
kind: 'scalar',
|
|
1330
|
+
T: 5 /*ScalarType.INT32*/,
|
|
1331
|
+
},
|
|
1240
1332
|
]);
|
|
1241
1333
|
}
|
|
1242
1334
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds unique values to an array.
|
|
3
|
+
*
|
|
4
|
+
* @param arr the array to add to.
|
|
5
|
+
* @param values the values to add.
|
|
6
|
+
*/
|
|
7
|
+
export const pushToIfMissing = <T>(arr: T[], ...values: T[]): T[] => {
|
|
8
|
+
for (const v of values) {
|
|
9
|
+
if (!arr.includes(v)) {
|
|
10
|
+
arr.push(v);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return arr;
|
|
14
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { CallSettingsResponse, OwnCapability } from '../gen/coordinator';
|
|
2
|
+
import { TrackType } from '../gen/video/sfu/models/models';
|
|
3
|
+
import { ensureExhausted } from '../helpers/ensureExhausted';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Stores the permissions for the current user and exposes
|
|
@@ -39,6 +41,26 @@ export class PermissionsContext {
|
|
|
39
41
|
return this.permissions.includes(permission);
|
|
40
42
|
};
|
|
41
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Helper method that checks whether the current user has the permission
|
|
46
|
+
* to publish the given track type.
|
|
47
|
+
*/
|
|
48
|
+
canPublish = (trackType: TrackType) => {
|
|
49
|
+
switch (trackType) {
|
|
50
|
+
case TrackType.AUDIO:
|
|
51
|
+
return this.hasPermission(OwnCapability.SEND_AUDIO);
|
|
52
|
+
case TrackType.VIDEO:
|
|
53
|
+
return this.hasPermission(OwnCapability.SEND_VIDEO);
|
|
54
|
+
case TrackType.SCREEN_SHARE:
|
|
55
|
+
case TrackType.SCREEN_SHARE_AUDIO:
|
|
56
|
+
return this.hasPermission(OwnCapability.SCREENSHARE);
|
|
57
|
+
case TrackType.UNSPECIFIED:
|
|
58
|
+
return false;
|
|
59
|
+
default:
|
|
60
|
+
ensureExhausted(trackType, 'Unknown track type');
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
42
64
|
/**
|
|
43
65
|
* Checks if the current user can request a specific permission
|
|
44
66
|
* within the call.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { TrackType } from '../../gen/video/sfu/models/models';
|
|
3
|
+
import { CallSettingsResponse, OwnCapability } from '../../gen/coordinator';
|
|
4
|
+
import { PermissionsContext } from '../PermissionsContext';
|
|
5
|
+
|
|
6
|
+
describe('PermissionsContext', () => {
|
|
7
|
+
it('should set permissions', () => {
|
|
8
|
+
const ctx = new PermissionsContext();
|
|
9
|
+
ctx.setPermissions([OwnCapability.SEND_AUDIO]);
|
|
10
|
+
expect(ctx.hasPermission(OwnCapability.SEND_AUDIO)).toBe(true);
|
|
11
|
+
expect(ctx.hasPermission(OwnCapability.SEND_VIDEO)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should set call settings', () => {
|
|
15
|
+
const ctx = new PermissionsContext();
|
|
16
|
+
const settings: CallSettingsResponse = {
|
|
17
|
+
// @ts-expect-error incomplete settings
|
|
18
|
+
audio: { access_request_enabled: true },
|
|
19
|
+
// @ts-expect-error incomplete settings
|
|
20
|
+
video: { access_request_enabled: false },
|
|
21
|
+
// @ts-expect-error incomplete settings
|
|
22
|
+
screensharing: { access_request_enabled: false },
|
|
23
|
+
};
|
|
24
|
+
ctx.setCallSettings(settings);
|
|
25
|
+
expect(ctx.canRequest(OwnCapability.SEND_AUDIO)).toBe(true);
|
|
26
|
+
expect(ctx.canRequest(OwnCapability.SEND_VIDEO)).toBe(false);
|
|
27
|
+
expect(ctx.canRequest(OwnCapability.SCREENSHARE)).toBe(false);
|
|
28
|
+
expect(ctx.canRequest(OwnCapability.BLOCK_USERS)).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should check if user can publish', () => {
|
|
32
|
+
const ctx = new PermissionsContext();
|
|
33
|
+
ctx.setPermissions([OwnCapability.SEND_AUDIO]);
|
|
34
|
+
expect(ctx.canPublish(TrackType.AUDIO)).toBe(true);
|
|
35
|
+
expect(ctx.canPublish(TrackType.VIDEO)).toBe(false);
|
|
36
|
+
expect(ctx.canPublish(TrackType.SCREEN_SHARE)).toBe(false);
|
|
37
|
+
expect(ctx.canPublish(TrackType.SCREEN_SHARE_AUDIO)).toBe(false);
|
|
38
|
+
expect(ctx.canPublish(TrackType.UNSPECIFIED)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
createSignalClient,
|
|
4
|
+
withHeaders,
|
|
5
|
+
withRequestLogger,
|
|
6
|
+
} from '../createClient';
|
|
7
|
+
import { TwirpFetchTransport } from '@protobuf-ts/twirp-transport';
|
|
8
|
+
|
|
9
|
+
describe('createClient', () => {
|
|
10
|
+
it('should create a client with TwirpFetchTransport', () => {
|
|
11
|
+
const client = createSignalClient();
|
|
12
|
+
expect(client).toBeDefined();
|
|
13
|
+
// @ts-expect-error - private field
|
|
14
|
+
expect(client._transport).toBeDefined();
|
|
15
|
+
// @ts-expect-error - private field
|
|
16
|
+
expect(client._transport).toBeInstanceOf(TwirpFetchTransport);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('withHeaders should add headers to the request', () => {
|
|
20
|
+
const headers = { Authorization: 'Bearer token' };
|
|
21
|
+
const interceptor = withHeaders(headers);
|
|
22
|
+
const next = vi.fn();
|
|
23
|
+
interceptor.interceptUnary(next, null, null, { meta: {} });
|
|
24
|
+
expect(next).toHaveBeenCalled();
|
|
25
|
+
expect(next.mock.lastCall.at(-1).meta).toEqual(headers);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('withRequestLogger should log the request', () => {
|
|
29
|
+
const logger = vi.fn();
|
|
30
|
+
const level = 'debug';
|
|
31
|
+
const interceptor = withRequestLogger(logger, level);
|
|
32
|
+
const next = vi.fn();
|
|
33
|
+
// @ts-expect-error - private field
|
|
34
|
+
interceptor.interceptUnary(next, { name: 'test' }, null, null);
|
|
35
|
+
expect(next).toHaveBeenCalled();
|
|
36
|
+
expect(logger).toHaveBeenCalled();
|
|
37
|
+
});
|
|
38
|
+
});
|
package/src/rpc/createClient.ts
CHANGED
|
@@ -48,11 +48,17 @@ export const withRequestLogger = (
|
|
|
48
48
|
input: object,
|
|
49
49
|
options: RpcOptions,
|
|
50
50
|
): UnaryCall => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
options
|
|
54
|
-
}
|
|
55
|
-
|
|
51
|
+
let invocation: UnaryCall | undefined;
|
|
52
|
+
try {
|
|
53
|
+
invocation = next(method, input, options);
|
|
54
|
+
} finally {
|
|
55
|
+
logger(level, `Invoked SFU RPC method ${method.name}`, {
|
|
56
|
+
request: invocation?.request,
|
|
57
|
+
headers: invocation?.requestHeaders,
|
|
58
|
+
response: invocation?.response,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return invocation;
|
|
56
62
|
},
|
|
57
63
|
};
|
|
58
64
|
};
|