@stream-io/video-client 0.1.11 → 0.2.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.
@@ -317,6 +317,25 @@ export interface BroadcastSettings {
317
317
  */
318
318
  hls: HLSSettings;
319
319
  }
320
+ /**
321
+ *
322
+ * @export
323
+ * @interface BroadcastSettingsRequest
324
+ */
325
+ export interface BroadcastSettingsRequest {
326
+ /**
327
+ *
328
+ * @type {boolean}
329
+ * @memberof BroadcastSettingsRequest
330
+ */
331
+ enabled?: boolean;
332
+ /**
333
+ *
334
+ * @type {HLSSettingsRequest}
335
+ * @memberof BroadcastSettingsRequest
336
+ */
337
+ hls?: HLSSettingsRequest;
338
+ }
320
339
  /**
321
340
  * This event is sent when a user accepts a notification to join a call.
322
341
  * @export
@@ -1340,6 +1359,12 @@ export interface CallSettingsRequest {
1340
1359
  * @memberof CallSettingsRequest
1341
1360
  */
1342
1361
  backstage?: BackstageSettingsRequest;
1362
+ /**
1363
+ *
1364
+ * @type {BroadcastSettingsRequest}
1365
+ * @memberof CallSettingsRequest
1366
+ */
1367
+ broadcasting?: BroadcastSettingsRequest;
1343
1368
  /**
1344
1369
  *
1345
1370
  * @type {GeofenceSettingsRequest}
@@ -2413,6 +2438,31 @@ export interface HLSSettings {
2413
2438
  */
2414
2439
  quality_tracks: Array<string>;
2415
2440
  }
2441
+ /**
2442
+ *
2443
+ * @export
2444
+ * @interface HLSSettingsRequest
2445
+ */
2446
+ export interface HLSSettingsRequest {
2447
+ /**
2448
+ *
2449
+ * @type {boolean}
2450
+ * @memberof HLSSettingsRequest
2451
+ */
2452
+ auto_on?: boolean;
2453
+ /**
2454
+ *
2455
+ * @type {boolean}
2456
+ * @memberof HLSSettingsRequest
2457
+ */
2458
+ enabled?: boolean;
2459
+ /**
2460
+ *
2461
+ * @type {Array<string>}
2462
+ * @memberof HLSSettingsRequest
2463
+ */
2464
+ quality_tracks?: Array<string>;
2465
+ }
2416
2466
  /**
2417
2467
  *
2418
2468
  * @export
@@ -2836,6 +2886,7 @@ export const OwnCapability = {
2836
2886
  JOIN_CALL: 'join-call',
2837
2887
  JOIN_ENDED_CALL: 'join-ended-call',
2838
2888
  MUTE_USERS: 'mute-users',
2889
+ PIN_FOR_EVERYONE: 'pin-for-everyone',
2839
2890
  READ_CALL: 'read-call',
2840
2891
  REMOVE_CALL_MEMBER: 'remove-call-member',
2841
2892
  SCREENSHARE: 'screenshare',
@@ -2959,6 +3010,38 @@ export interface PermissionRequestEvent {
2959
3010
  */
2960
3011
  user: UserResponse;
2961
3012
  }
3013
+ /**
3014
+ *
3015
+ * @export
3016
+ * @interface PinRequest
3017
+ */
3018
+ export interface PinRequest {
3019
+ /**
3020
+ *
3021
+ * @type {string}
3022
+ * @memberof PinRequest
3023
+ */
3024
+ session_id: string;
3025
+ /**
3026
+ *
3027
+ * @type {string}
3028
+ * @memberof PinRequest
3029
+ */
3030
+ user_id: string;
3031
+ }
3032
+ /**
3033
+ *
3034
+ * @export
3035
+ * @interface PinResponse
3036
+ */
3037
+ export interface PinResponse {
3038
+ /**
3039
+ * Duration of the request in human-readable format
3040
+ * @type {string}
3041
+ * @memberof PinResponse
3042
+ */
3043
+ duration: string;
3044
+ }
2962
3045
  /**
2963
3046
  *
2964
3047
  * @export
@@ -3774,6 +3857,38 @@ export interface UnblockedUserEvent {
3774
3857
  */
3775
3858
  user: UserResponse;
3776
3859
  }
3860
+ /**
3861
+ *
3862
+ * @export
3863
+ * @interface UnpinRequest
3864
+ */
3865
+ export interface UnpinRequest {
3866
+ /**
3867
+ *
3868
+ * @type {string}
3869
+ * @memberof UnpinRequest
3870
+ */
3871
+ session_id: string;
3872
+ /**
3873
+ *
3874
+ * @type {string}
3875
+ * @memberof UnpinRequest
3876
+ */
3877
+ user_id: string;
3878
+ }
3879
+ /**
3880
+ *
3881
+ * @export
3882
+ * @interface UnpinResponse
3883
+ */
3884
+ export interface UnpinResponse {
3885
+ /**
3886
+ * Duration of the request in human-readable format
3887
+ * @type {string}
3888
+ * @memberof UnpinResponse
3889
+ */
3890
+ duration: string;
3891
+ }
3777
3892
  /**
3778
3893
  *
3779
3894
  * @export
@@ -28,6 +28,7 @@ import {
28
28
  Participant,
29
29
  ParticipantCount,
30
30
  PeerType,
31
+ Pin,
31
32
  TrackInfo,
32
33
  TrackType,
33
34
  TrackUnpublishReason,
@@ -218,10 +219,31 @@ export interface SfuEvent {
218
219
  */
219
220
  iceRestart: ICERestart;
220
221
  }
222
+ | {
223
+ oneofKind: 'pinsUpdated';
224
+ /**
225
+ * PinsChanged is sent the list of pins in the call changes. This event contains the entire list of pins.
226
+ *
227
+ * @generated from protobuf field: stream.video.sfu.event.PinsChanged pins_updated = 22;
228
+ */
229
+ pinsUpdated: PinsChanged;
230
+ }
221
231
  | {
222
232
  oneofKind: undefined;
223
233
  };
224
234
  }
235
+ /**
236
+ * @generated from protobuf message stream.video.sfu.event.PinsChanged
237
+ */
238
+ export interface PinsChanged {
239
+ /**
240
+ * the list of pins in the call.
241
+ * Pins are ordered in descending order (most important first).
242
+ *
243
+ * @generated from protobuf field: repeated stream.video.sfu.models.Pin pins = 1;
244
+ */
245
+ pins: Pin[];
246
+ }
225
247
  /**
226
248
  * @generated from protobuf message stream.video.sfu.event.Error
227
249
  */
@@ -844,6 +866,13 @@ class SfuEvent$Type extends MessageType<SfuEvent> {
844
866
  oneof: 'eventPayload',
845
867
  T: () => ICERestart,
846
868
  },
869
+ {
870
+ no: 22,
871
+ name: 'pins_updated',
872
+ kind: 'message',
873
+ oneof: 'eventPayload',
874
+ T: () => PinsChanged,
875
+ },
847
876
  ]);
848
877
  }
849
878
  create(value?: PartialMessage<SfuEvent>): SfuEvent {
@@ -1055,6 +1084,17 @@ class SfuEvent$Type extends MessageType<SfuEvent> {
1055
1084
  ),
1056
1085
  };
1057
1086
  break;
1087
+ case /* stream.video.sfu.event.PinsChanged pins_updated */ 22:
1088
+ message.eventPayload = {
1089
+ oneofKind: 'pinsUpdated',
1090
+ pinsUpdated: PinsChanged.internalBinaryRead(
1091
+ reader,
1092
+ reader.uint32(),
1093
+ options,
1094
+ (message.eventPayload as any).pinsUpdated,
1095
+ ),
1096
+ };
1097
+ break;
1058
1098
  default:
1059
1099
  let u = options.readUnknownField;
1060
1100
  if (u === 'throw')
@@ -1198,6 +1238,13 @@ class SfuEvent$Type extends MessageType<SfuEvent> {
1198
1238
  writer.tag(21, WireType.LengthDelimited).fork(),
1199
1239
  options,
1200
1240
  ).join();
1241
+ /* stream.video.sfu.event.PinsChanged pins_updated = 22; */
1242
+ if (message.eventPayload.oneofKind === 'pinsUpdated')
1243
+ PinsChanged.internalBinaryWrite(
1244
+ message.eventPayload.pinsUpdated,
1245
+ writer.tag(22, WireType.LengthDelimited).fork(),
1246
+ options,
1247
+ ).join();
1201
1248
  let u = options.writeUnknownFields;
1202
1249
  if (u !== false)
1203
1250
  (u == true ? UnknownFieldHandler.onWrite : u)(
@@ -1213,6 +1260,90 @@ class SfuEvent$Type extends MessageType<SfuEvent> {
1213
1260
  */
1214
1261
  export const SfuEvent = new SfuEvent$Type();
1215
1262
  // @generated message type with reflection information, may provide speed optimized methods
1263
+ class PinsChanged$Type extends MessageType<PinsChanged> {
1264
+ constructor() {
1265
+ super('stream.video.sfu.event.PinsChanged', [
1266
+ {
1267
+ no: 1,
1268
+ name: 'pins',
1269
+ kind: 'message',
1270
+ repeat: 1 /*RepeatType.PACKED*/,
1271
+ T: () => Pin,
1272
+ },
1273
+ ]);
1274
+ }
1275
+ create(value?: PartialMessage<PinsChanged>): PinsChanged {
1276
+ const message = { pins: [] };
1277
+ globalThis.Object.defineProperty(message, MESSAGE_TYPE, {
1278
+ enumerable: false,
1279
+ value: this,
1280
+ });
1281
+ if (value !== undefined)
1282
+ reflectionMergePartial<PinsChanged>(this, message, value);
1283
+ return message;
1284
+ }
1285
+ internalBinaryRead(
1286
+ reader: IBinaryReader,
1287
+ length: number,
1288
+ options: BinaryReadOptions,
1289
+ target?: PinsChanged,
1290
+ ): PinsChanged {
1291
+ let message = target ?? this.create(),
1292
+ end = reader.pos + length;
1293
+ while (reader.pos < end) {
1294
+ let [fieldNo, wireType] = reader.tag();
1295
+ switch (fieldNo) {
1296
+ case /* repeated stream.video.sfu.models.Pin pins */ 1:
1297
+ message.pins.push(
1298
+ Pin.internalBinaryRead(reader, reader.uint32(), options),
1299
+ );
1300
+ break;
1301
+ default:
1302
+ let u = options.readUnknownField;
1303
+ if (u === 'throw')
1304
+ throw new globalThis.Error(
1305
+ `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`,
1306
+ );
1307
+ let d = reader.skip(wireType);
1308
+ if (u !== false)
1309
+ (u === true ? UnknownFieldHandler.onRead : u)(
1310
+ this.typeName,
1311
+ message,
1312
+ fieldNo,
1313
+ wireType,
1314
+ d,
1315
+ );
1316
+ }
1317
+ }
1318
+ return message;
1319
+ }
1320
+ internalBinaryWrite(
1321
+ message: PinsChanged,
1322
+ writer: IBinaryWriter,
1323
+ options: BinaryWriteOptions,
1324
+ ): IBinaryWriter {
1325
+ /* repeated stream.video.sfu.models.Pin pins = 1; */
1326
+ for (let i = 0; i < message.pins.length; i++)
1327
+ Pin.internalBinaryWrite(
1328
+ message.pins[i],
1329
+ writer.tag(1, WireType.LengthDelimited).fork(),
1330
+ options,
1331
+ ).join();
1332
+ let u = options.writeUnknownFields;
1333
+ if (u !== false)
1334
+ (u == true ? UnknownFieldHandler.onWrite : u)(
1335
+ this.typeName,
1336
+ message,
1337
+ writer,
1338
+ );
1339
+ return writer;
1340
+ }
1341
+ }
1342
+ /**
1343
+ * @generated MessageType for protobuf message stream.video.sfu.event.PinsChanged
1344
+ */
1345
+ export const PinsChanged = new PinsChanged$Type();
1346
+ // @generated message type with reflection information, may provide speed optimized methods
1216
1347
  class Error$Type extends MessageType<Error> {
1217
1348
  constructor() {
1218
1349
  super('stream.video.sfu.event.Error', [
@@ -48,6 +48,13 @@ export interface CallState {
48
48
  * @generated from protobuf field: stream.video.sfu.models.ParticipantCount participant_count = 3;
49
49
  */
50
50
  participantCount?: ParticipantCount;
51
+ /**
52
+ * the list of pins in the call.
53
+ * Pins are ordered in descending order (most important first).
54
+ *
55
+ * @generated from protobuf field: repeated stream.video.sfu.models.Pin pins = 4;
56
+ */
57
+ pins: Pin[];
51
58
  }
52
59
  /**
53
60
  * @generated from protobuf message stream.video.sfu.models.ParticipantCount
@@ -67,6 +74,23 @@ export interface ParticipantCount {
67
74
  */
68
75
  anonymous: number;
69
76
  }
77
+ /**
78
+ * @generated from protobuf message stream.video.sfu.models.Pin
79
+ */
80
+ export interface Pin {
81
+ /**
82
+ * the user to pin
83
+ *
84
+ * @generated from protobuf field: string user_id = 1;
85
+ */
86
+ userId: string;
87
+ /**
88
+ * the user sesion_id to pin, if not provided, applies to all sessions
89
+ *
90
+ * @generated from protobuf field: string session_id = 2;
91
+ */
92
+ sessionId: string;
93
+ }
70
94
  /**
71
95
  * those who are online in the call
72
96
  *
@@ -695,10 +719,17 @@ class CallState$Type extends MessageType<CallState> {
695
719
  kind: 'message',
696
720
  T: () => ParticipantCount,
697
721
  },
722
+ {
723
+ no: 4,
724
+ name: 'pins',
725
+ kind: 'message',
726
+ repeat: 1 /*RepeatType.PACKED*/,
727
+ T: () => Pin,
728
+ },
698
729
  ]);
699
730
  }
700
731
  create(value?: PartialMessage<CallState>): CallState {
701
- const message = { participants: [] };
732
+ const message = { participants: [], pins: [] };
702
733
  globalThis.Object.defineProperty(message, MESSAGE_TYPE, {
703
734
  enumerable: false,
704
735
  value: this,
@@ -739,6 +770,11 @@ class CallState$Type extends MessageType<CallState> {
739
770
  message.participantCount,
740
771
  );
741
772
  break;
773
+ case /* repeated stream.video.sfu.models.Pin pins */ 4:
774
+ message.pins.push(
775
+ Pin.internalBinaryRead(reader, reader.uint32(), options),
776
+ );
777
+ break;
742
778
  default:
743
779
  let u = options.readUnknownField;
744
780
  if (u === 'throw')
@@ -784,6 +820,13 @@ class CallState$Type extends MessageType<CallState> {
784
820
  writer.tag(3, WireType.LengthDelimited).fork(),
785
821
  options,
786
822
  ).join();
823
+ /* repeated stream.video.sfu.models.Pin pins = 4; */
824
+ for (let i = 0; i < message.pins.length; i++)
825
+ Pin.internalBinaryWrite(
826
+ message.pins[i],
827
+ writer.tag(4, WireType.LengthDelimited).fork(),
828
+ options,
829
+ ).join();
787
830
  let u = options.writeUnknownFields;
788
831
  if (u !== false)
789
832
  (u == true ? UnknownFieldHandler.onWrite : u)(
@@ -878,6 +921,84 @@ class ParticipantCount$Type extends MessageType<ParticipantCount> {
878
921
  */
879
922
  export const ParticipantCount = new ParticipantCount$Type();
880
923
  // @generated message type with reflection information, may provide speed optimized methods
924
+ class Pin$Type extends MessageType<Pin> {
925
+ constructor() {
926
+ super('stream.video.sfu.models.Pin', [
927
+ { no: 1, name: 'user_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
928
+ { no: 2, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
929
+ ]);
930
+ }
931
+ create(value?: PartialMessage<Pin>): Pin {
932
+ const message = { userId: '', sessionId: '' };
933
+ globalThis.Object.defineProperty(message, MESSAGE_TYPE, {
934
+ enumerable: false,
935
+ value: this,
936
+ });
937
+ if (value !== undefined) reflectionMergePartial<Pin>(this, message, value);
938
+ return message;
939
+ }
940
+ internalBinaryRead(
941
+ reader: IBinaryReader,
942
+ length: number,
943
+ options: BinaryReadOptions,
944
+ target?: Pin,
945
+ ): Pin {
946
+ let message = target ?? this.create(),
947
+ end = reader.pos + length;
948
+ while (reader.pos < end) {
949
+ let [fieldNo, wireType] = reader.tag();
950
+ switch (fieldNo) {
951
+ case /* string user_id */ 1:
952
+ message.userId = reader.string();
953
+ break;
954
+ case /* string session_id */ 2:
955
+ message.sessionId = reader.string();
956
+ break;
957
+ default:
958
+ let u = options.readUnknownField;
959
+ if (u === 'throw')
960
+ throw new globalThis.Error(
961
+ `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`,
962
+ );
963
+ let d = reader.skip(wireType);
964
+ if (u !== false)
965
+ (u === true ? UnknownFieldHandler.onRead : u)(
966
+ this.typeName,
967
+ message,
968
+ fieldNo,
969
+ wireType,
970
+ d,
971
+ );
972
+ }
973
+ }
974
+ return message;
975
+ }
976
+ internalBinaryWrite(
977
+ message: Pin,
978
+ writer: IBinaryWriter,
979
+ options: BinaryWriteOptions,
980
+ ): IBinaryWriter {
981
+ /* string user_id = 1; */
982
+ if (message.userId !== '')
983
+ writer.tag(1, WireType.LengthDelimited).string(message.userId);
984
+ /* string session_id = 2; */
985
+ if (message.sessionId !== '')
986
+ writer.tag(2, WireType.LengthDelimited).string(message.sessionId);
987
+ let u = options.writeUnknownFields;
988
+ if (u !== false)
989
+ (u == true ? UnknownFieldHandler.onWrite : u)(
990
+ this.typeName,
991
+ message,
992
+ writer,
993
+ );
994
+ return writer;
995
+ }
996
+ }
997
+ /**
998
+ * @generated MessageType for protobuf message stream.video.sfu.models.Pin
999
+ */
1000
+ export const Pin = new Pin$Type();
1001
+ // @generated message type with reflection information, may provide speed optimized methods
881
1002
  class Participant$Type extends MessageType<Participant> {
882
1003
  constructor() {
883
1004
  super('stream.video.sfu.models.Participant', [
@@ -22,6 +22,7 @@ const sfuEventKinds: { [key in SfuEventKinds]: undefined } = {
22
22
  callGrantsUpdated: undefined,
23
23
  goAway: undefined,
24
24
  iceRestart: undefined,
25
+ pinsUpdated: undefined,
25
26
  };
26
27
 
27
28
  export const isSfuEvent = (
@@ -112,15 +112,9 @@ export const reconcileParticipantLocalState = (
112
112
  ) => {
113
113
  if (!source) return target;
114
114
 
115
- target.audioStream = source.audioStream;
116
- target.videoStream = source.videoStream;
117
- target.screenShareStream = source.screenShareStream;
115
+ // copy everything from source to target
116
+ Object.assign(target, source);
118
117
 
119
- target.videoDimension = source.videoDimension;
120
- target.screenShareDimension = source.screenShareDimension;
121
- target.pinnedAt = source.pinnedAt;
122
- target.reaction = source.reaction;
123
- target.viewportVisibilityState = source.viewportVisibilityState;
124
118
  if (
125
119
  isStreamVideoLocalParticipant(source) &&
126
120
  isStreamVideoLocalParticipant(target)
@@ -92,7 +92,10 @@ export const participants = (): StreamVideoParticipant[] => [
92
92
  isDominantSpeaker: false,
93
93
  audioLevel: 0,
94
94
  image: '',
95
- pinnedAt: Date.now(),
95
+ pin: {
96
+ isLocalPin: true,
97
+ pinnedAt: Date.now(),
98
+ },
96
99
  roles: [],
97
100
  viewportVisibilityState: VisibilityState.VISIBLE,
98
101
  },
@@ -69,13 +69,15 @@ export const publishingAudio: Comparator<StreamVideoParticipant> = (a, b) => {
69
69
  * @param b the second participant.
70
70
  */
71
71
  export const pinned: Comparator<StreamVideoParticipant> = (a, b) => {
72
- if (a.pinnedAt && b.pinnedAt) {
73
- if (a.pinnedAt > b.pinnedAt) return -1;
74
- if (a.pinnedAt < b.pinnedAt) return 1;
72
+ if (a.pin && b.pin) {
73
+ if (!a.pin.isLocalPin && b.pin.isLocalPin) return -1;
74
+ if (a.pin.isLocalPin && !b.pin.isLocalPin) return 1;
75
+ if (a.pin.pinnedAt > b.pin.pinnedAt) return -1;
76
+ if (a.pin.pinnedAt < b.pin.pinnedAt) return 1;
75
77
  }
76
78
 
77
- if (a.pinnedAt && !b.pinnedAt) return -1;
78
- if (!a.pinnedAt && b.pinnedAt) return 1;
79
+ if (a.pin && !b.pin) return -1;
80
+ if (!a.pin && b.pin) return 1;
79
81
 
80
82
  return 0;
81
83
  };
@@ -16,7 +16,7 @@ import {
16
16
  MemberResponse,
17
17
  OwnCapability,
18
18
  } from '../gen/coordinator';
19
- import { TrackType } from '../gen/video/sfu/models/models';
19
+ import { Pin, TrackType } from '../gen/video/sfu/models/models';
20
20
  import { Comparator } from '../sorting';
21
21
  import * as SortingPreset from '../sorting/presets';
22
22
  import { getLogger } from '../logger';
@@ -287,7 +287,7 @@ export class CallState {
287
287
  );
288
288
 
289
289
  this.pinnedParticipants$ = this.participants$.pipe(
290
- map((participants) => participants.filter((p) => p.pinnedAt)),
290
+ map((participants) => participants.filter((p) => !!p.pin)),
291
291
  );
292
292
 
293
293
  this.dominantSpeaker$ = this.participants$.pipe(
@@ -678,4 +678,45 @@ export class CallState {
678
678
  }),
679
679
  );
680
680
  };
681
+
682
+ /**
683
+ * Updates the participant pinned state with server side pinning data.
684
+ *
685
+ * @param pins the latest pins from the server.
686
+ */
687
+ setServerSidePins = (pins: Pin[]) => {
688
+ const pinsLookup = pins.reduce<{ [sessionId: string]: number | undefined }>(
689
+ (lookup, pin) => {
690
+ lookup[pin.sessionId] = Date.now();
691
+ return lookup;
692
+ },
693
+ {},
694
+ );
695
+
696
+ return this.setParticipants((participants) =>
697
+ participants.map((participant) => {
698
+ const serverSidePinnedAt = pinsLookup[participant.sessionId];
699
+ // the participant is newly pinned
700
+ if (serverSidePinnedAt) {
701
+ return {
702
+ ...participant,
703
+ pin: {
704
+ isLocalPin: false,
705
+ pinnedAt: serverSidePinnedAt,
706
+ },
707
+ };
708
+ }
709
+ // the participant is no longer pinned server side
710
+ // we need to reset the pin
711
+ if (participant.pin && !participant.pin.isLocalPin) {
712
+ return {
713
+ ...participant,
714
+ pin: undefined,
715
+ };
716
+ }
717
+ // no changes to be applied
718
+ return participant;
719
+ }),
720
+ );
721
+ };
681
722
  }