@stream-io/video-client 1.26.1 → 1.27.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 +18 -0
- package/dist/index.browser.es.js +276 -73
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +275 -71
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +276 -73
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +14 -2
- package/dist/src/StreamSfuClient.d.ts +7 -3
- package/dist/src/devices/devices.d.ts +5 -5
- package/dist/src/events/internal.d.ts +7 -1
- package/dist/src/gen/video/sfu/event/events.d.ts +57 -1
- package/dist/src/gen/video/sfu/models/models.d.ts +21 -0
- package/dist/src/helpers/array.d.ts +7 -0
- package/dist/src/helpers/lazy.d.ts +1 -1
- package/dist/src/helpers/participantUtils.d.ts +8 -1
- package/dist/src/rtc/BasePeerConnection.d.ts +2 -2
- package/dist/src/rtc/Dispatcher.d.ts +1 -1
- package/dist/src/rtc/signal.d.ts +1 -1
- package/dist/src/stats/rtc/Tracer.d.ts +4 -1
- package/dist/src/stats/rtc/types.d.ts +1 -0
- package/dist/src/store/CallState.d.ts +2 -1
- package/dist/src/timers/index.d.ts +1 -1
- package/dist/src/types.d.ts +10 -1
- package/package.json +2 -2
- package/src/Call.ts +55 -9
- package/src/StreamSfuClient.ts +33 -14
- package/src/coordinator/connection/connection.ts +0 -2
- package/src/devices/CameraManager.ts +1 -1
- package/src/devices/InputMediaDeviceManager.ts +5 -3
- package/src/devices/MicrophoneManager.ts +2 -1
- package/src/devices/SpeakerManager.ts +1 -1
- package/src/devices/devices.ts +29 -11
- package/src/events/__tests__/internal.test.ts +78 -0
- package/src/events/__tests__/participant.test.ts +66 -0
- package/src/events/callEventHandlers.ts +2 -0
- package/src/events/internal.ts +28 -1
- package/src/events/participant.ts +4 -1
- package/src/gen/video/sfu/event/events.ts +104 -0
- package/src/gen/video/sfu/models/models.ts +21 -0
- package/src/helpers/__tests__/participantUtils.test.ts +167 -0
- package/src/helpers/array.ts +16 -0
- package/src/helpers/lazy.ts +3 -3
- package/src/helpers/participantUtils.ts +23 -1
- package/src/rtc/BasePeerConnection.ts +6 -5
- package/src/rtc/Dispatcher.ts +3 -2
- package/src/rtc/__tests__/Publisher.test.ts +3 -2
- package/src/rtc/__tests__/Subscriber.test.ts +3 -2
- package/src/rtc/__tests__/videoLayers.test.ts +4 -6
- package/src/rtc/signal.ts +3 -3
- package/src/rtc/videoLayers.ts +12 -6
- package/src/stats/rtc/Tracer.ts +19 -1
- package/src/stats/rtc/types.ts +1 -0
- package/src/store/CallState.ts +7 -4
- package/src/types.ts +11 -0
package/dist/index.es.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ServiceType, stackIntercept, RpcError } from '@protobuf-ts/runtime-rpc'
|
|
|
4
4
|
import axios from 'axios';
|
|
5
5
|
export { AxiosError } from 'axios';
|
|
6
6
|
import { TwirpFetchTransport, TwirpErrorCode } from '@protobuf-ts/twirp-transport';
|
|
7
|
-
import { ReplaySubject, combineLatest, BehaviorSubject, shareReplay, map, distinctUntilChanged, takeWhile, distinctUntilKeyChanged, fromEventPattern, startWith, concatMap, merge, from, fromEvent, debounceTime, pairwise, of } from 'rxjs';
|
|
7
|
+
import { ReplaySubject, combineLatest, BehaviorSubject, shareReplay, map, distinctUntilChanged, takeWhile, distinctUntilKeyChanged, fromEventPattern, startWith, concatMap, merge, from, fromEvent, tap, debounceTime, pairwise, of } from 'rxjs';
|
|
8
8
|
import { UAParser } from 'ua-parser-js';
|
|
9
9
|
import { parse, write } from 'sdp-transform';
|
|
10
10
|
import https from 'https';
|
|
@@ -972,6 +972,10 @@ var ErrorCode;
|
|
|
972
972
|
* @generated from protobuf enum value: ERROR_CODE_CALL_NOT_FOUND = 300;
|
|
973
973
|
*/
|
|
974
974
|
ErrorCode[ErrorCode["CALL_NOT_FOUND"] = 300] = "CALL_NOT_FOUND";
|
|
975
|
+
/**
|
|
976
|
+
* @generated from protobuf enum value: ERROR_CODE_CALL_PARTICIPANT_LIMIT_REACHED = 301;
|
|
977
|
+
*/
|
|
978
|
+
ErrorCode[ErrorCode["CALL_PARTICIPANT_LIMIT_REACHED"] = 301] = "CALL_PARTICIPANT_LIMIT_REACHED";
|
|
975
979
|
/**
|
|
976
980
|
* @generated from protobuf enum value: ERROR_CODE_REQUEST_VALIDATION_FAILED = 400;
|
|
977
981
|
*/
|
|
@@ -1240,6 +1244,24 @@ var AppleThermalState;
|
|
|
1240
1244
|
*/
|
|
1241
1245
|
AppleThermalState[AppleThermalState["CRITICAL"] = 4] = "CRITICAL";
|
|
1242
1246
|
})(AppleThermalState || (AppleThermalState = {}));
|
|
1247
|
+
/**
|
|
1248
|
+
* ClientCapability defines a feature that client supports
|
|
1249
|
+
*
|
|
1250
|
+
* @generated from protobuf enum stream.video.sfu.models.ClientCapability
|
|
1251
|
+
*/
|
|
1252
|
+
var ClientCapability;
|
|
1253
|
+
(function (ClientCapability) {
|
|
1254
|
+
/**
|
|
1255
|
+
* @generated from protobuf enum value: CLIENT_CAPABILITY_UNSPECIFIED = 0;
|
|
1256
|
+
*/
|
|
1257
|
+
ClientCapability[ClientCapability["UNSPECIFIED"] = 0] = "UNSPECIFIED";
|
|
1258
|
+
/**
|
|
1259
|
+
* Enables SFU pausing inbound video
|
|
1260
|
+
*
|
|
1261
|
+
* @generated from protobuf enum value: CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE = 1;
|
|
1262
|
+
*/
|
|
1263
|
+
ClientCapability[ClientCapability["SUBSCRIBER_VIDEO_PAUSE"] = 1] = "SUBSCRIBER_VIDEO_PAUSE";
|
|
1264
|
+
})(ClientCapability || (ClientCapability = {}));
|
|
1243
1265
|
// @generated message type with reflection information, may provide speed optimized methods
|
|
1244
1266
|
class CallState$Type extends MessageType {
|
|
1245
1267
|
constructor() {
|
|
@@ -1910,6 +1932,7 @@ var models = /*#__PURE__*/Object.freeze({
|
|
|
1910
1932
|
get CallEndedReason () { return CallEndedReason; },
|
|
1911
1933
|
CallGrants: CallGrants,
|
|
1912
1934
|
CallState: CallState$1,
|
|
1935
|
+
get ClientCapability () { return ClientCapability; },
|
|
1913
1936
|
ClientDetails: ClientDetails,
|
|
1914
1937
|
Codec: Codec,
|
|
1915
1938
|
get ConnectionQuality () { return ConnectionQuality; },
|
|
@@ -2599,6 +2622,13 @@ class SfuEvent$Type extends MessageType {
|
|
|
2599
2622
|
oneof: 'eventPayload',
|
|
2600
2623
|
T: () => ChangePublishOptions,
|
|
2601
2624
|
},
|
|
2625
|
+
{
|
|
2626
|
+
no: 28,
|
|
2627
|
+
name: 'inbound_state_notification',
|
|
2628
|
+
kind: 'message',
|
|
2629
|
+
oneof: 'eventPayload',
|
|
2630
|
+
T: () => InboundStateNotification,
|
|
2631
|
+
},
|
|
2602
2632
|
]);
|
|
2603
2633
|
}
|
|
2604
2634
|
}
|
|
@@ -2906,6 +2936,17 @@ class JoinRequest$Type extends MessageType {
|
|
|
2906
2936
|
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
2907
2937
|
T: () => SubscribeOption,
|
|
2908
2938
|
},
|
|
2939
|
+
{
|
|
2940
|
+
no: 11,
|
|
2941
|
+
name: 'capabilities',
|
|
2942
|
+
kind: 'enum',
|
|
2943
|
+
repeat: 1 /*RepeatType.PACKED*/,
|
|
2944
|
+
T: () => [
|
|
2945
|
+
'stream.video.sfu.models.ClientCapability',
|
|
2946
|
+
ClientCapability,
|
|
2947
|
+
'CLIENT_CAPABILITY_',
|
|
2948
|
+
],
|
|
2949
|
+
},
|
|
2909
2950
|
]);
|
|
2910
2951
|
}
|
|
2911
2952
|
}
|
|
@@ -3351,6 +3392,48 @@ class CallEnded$Type extends MessageType {
|
|
|
3351
3392
|
* @generated MessageType for protobuf message stream.video.sfu.event.CallEnded
|
|
3352
3393
|
*/
|
|
3353
3394
|
const CallEnded = new CallEnded$Type();
|
|
3395
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
3396
|
+
class InboundStateNotification$Type extends MessageType {
|
|
3397
|
+
constructor() {
|
|
3398
|
+
super('stream.video.sfu.event.InboundStateNotification', [
|
|
3399
|
+
{
|
|
3400
|
+
no: 1,
|
|
3401
|
+
name: 'inbound_video_states',
|
|
3402
|
+
kind: 'message',
|
|
3403
|
+
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
3404
|
+
T: () => InboundVideoState,
|
|
3405
|
+
},
|
|
3406
|
+
]);
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
/**
|
|
3410
|
+
* @generated MessageType for protobuf message stream.video.sfu.event.InboundStateNotification
|
|
3411
|
+
*/
|
|
3412
|
+
const InboundStateNotification = new InboundStateNotification$Type();
|
|
3413
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
3414
|
+
class InboundVideoState$Type extends MessageType {
|
|
3415
|
+
constructor() {
|
|
3416
|
+
super('stream.video.sfu.event.InboundVideoState', [
|
|
3417
|
+
{ no: 1, name: 'user_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
3418
|
+
{ no: 2, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
3419
|
+
{
|
|
3420
|
+
no: 3,
|
|
3421
|
+
name: 'track_type',
|
|
3422
|
+
kind: 'enum',
|
|
3423
|
+
T: () => [
|
|
3424
|
+
'stream.video.sfu.models.TrackType',
|
|
3425
|
+
TrackType,
|
|
3426
|
+
'TRACK_TYPE_',
|
|
3427
|
+
],
|
|
3428
|
+
},
|
|
3429
|
+
{ no: 4, name: 'paused', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
|
|
3430
|
+
]);
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
/**
|
|
3434
|
+
* @generated MessageType for protobuf message stream.video.sfu.event.InboundVideoState
|
|
3435
|
+
*/
|
|
3436
|
+
const InboundVideoState = new InboundVideoState$Type();
|
|
3354
3437
|
|
|
3355
3438
|
var events = /*#__PURE__*/Object.freeze({
|
|
3356
3439
|
__proto__: null,
|
|
@@ -3371,6 +3454,8 @@ var events = /*#__PURE__*/Object.freeze({
|
|
|
3371
3454
|
HealthCheckResponse: HealthCheckResponse,
|
|
3372
3455
|
ICERestart: ICERestart,
|
|
3373
3456
|
ICETrickle: ICETrickle,
|
|
3457
|
+
InboundStateNotification: InboundStateNotification,
|
|
3458
|
+
InboundVideoState: InboundVideoState,
|
|
3374
3459
|
JoinRequest: JoinRequest,
|
|
3375
3460
|
JoinResponse: JoinResponse,
|
|
3376
3461
|
LeaveCallRequest: LeaveCallRequest,
|
|
@@ -3795,6 +3880,7 @@ const sfuEventKinds = {
|
|
|
3795
3880
|
participantUpdated: undefined,
|
|
3796
3881
|
participantMigrationComplete: undefined,
|
|
3797
3882
|
changePublishOptions: undefined,
|
|
3883
|
+
inboundStateNotification: undefined,
|
|
3798
3884
|
};
|
|
3799
3885
|
const isSfuEvent = (eventName) => {
|
|
3800
3886
|
return Object.prototype.hasOwnProperty.call(sfuEventKinds, eventName);
|
|
@@ -3803,12 +3889,12 @@ class Dispatcher {
|
|
|
3803
3889
|
constructor() {
|
|
3804
3890
|
this.logger = getLogger(['Dispatcher']);
|
|
3805
3891
|
this.subscribers = {};
|
|
3806
|
-
this.dispatch = (message,
|
|
3892
|
+
this.dispatch = (message, tag = '0') => {
|
|
3807
3893
|
const eventKind = message.eventPayload.oneofKind;
|
|
3808
3894
|
if (!eventKind)
|
|
3809
3895
|
return;
|
|
3810
3896
|
const payload = message.eventPayload[eventKind];
|
|
3811
|
-
this.logger('debug', `Dispatching ${eventKind}, tag=${
|
|
3897
|
+
this.logger('debug', `Dispatching ${eventKind}, tag=${tag}`, payload);
|
|
3812
3898
|
const listeners = this.subscribers[eventKind];
|
|
3813
3899
|
if (!listeners)
|
|
3814
3900
|
return;
|
|
@@ -4325,6 +4411,24 @@ const hasScreenShareAudio = (p) => p.publishedTracks.includes(TrackType.SCREEN_S
|
|
|
4325
4411
|
* @param p the participant.
|
|
4326
4412
|
*/
|
|
4327
4413
|
const isPinned = (p) => !!p.pin && (p.pin.isLocalPin || p.pin.pinnedAt > 0);
|
|
4414
|
+
/**
|
|
4415
|
+
* Check if a participant has a paused track of the specified type.
|
|
4416
|
+
*
|
|
4417
|
+
* @param p the participant to check.
|
|
4418
|
+
* @param videoTrackType the type of video track to check for ('videoTrack' or 'screenShareTrack').
|
|
4419
|
+
*/
|
|
4420
|
+
const hasPausedTrack = (p, videoTrackType) => {
|
|
4421
|
+
if (!p.pausedTracks)
|
|
4422
|
+
return false;
|
|
4423
|
+
const trackType = videoTrackType === 'videoTrack'
|
|
4424
|
+
? TrackType.VIDEO
|
|
4425
|
+
: videoTrackType === 'screenShareTrack'
|
|
4426
|
+
? TrackType.SCREEN_SHARE
|
|
4427
|
+
: undefined;
|
|
4428
|
+
if (!trackType)
|
|
4429
|
+
return false;
|
|
4430
|
+
return p.pausedTracks.includes(trackType);
|
|
4431
|
+
};
|
|
4328
4432
|
|
|
4329
4433
|
/**
|
|
4330
4434
|
* A comparator which sorts participants by the fact that they are the dominant speaker or not.
|
|
@@ -4727,17 +4831,17 @@ class CallState {
|
|
|
4727
4831
|
*
|
|
4728
4832
|
* @param sessionId the session ID of the participant to update.
|
|
4729
4833
|
* @param participant the participant to update or add.
|
|
4834
|
+
* @param patch an optional patch to apply to the participant.
|
|
4730
4835
|
*/
|
|
4731
|
-
this.updateOrAddParticipant = (sessionId, participant) => {
|
|
4836
|
+
this.updateOrAddParticipant = (sessionId, participant, patch) => {
|
|
4732
4837
|
return this.setParticipants((participants) => {
|
|
4733
4838
|
let add = true;
|
|
4734
4839
|
const nextParticipants = participants.map((p) => {
|
|
4735
4840
|
if (p.sessionId === sessionId) {
|
|
4736
4841
|
add = false;
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
};
|
|
4842
|
+
const updated = { ...p, ...participant };
|
|
4843
|
+
const thePatch = typeof patch === 'function' ? patch(updated) : patch;
|
|
4844
|
+
return Object.assign(updated, thePatch);
|
|
4741
4845
|
}
|
|
4742
4846
|
return p;
|
|
4743
4847
|
});
|
|
@@ -5811,7 +5915,7 @@ const aggregate = (stats) => {
|
|
|
5811
5915
|
return report;
|
|
5812
5916
|
};
|
|
5813
5917
|
|
|
5814
|
-
const version = "1.
|
|
5918
|
+
const version = "1.27.1";
|
|
5815
5919
|
const [major, minor, patch] = version.split('.');
|
|
5816
5920
|
let sdkInfo = {
|
|
5817
5921
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -6395,6 +6499,15 @@ class Tracer {
|
|
|
6395
6499
|
return;
|
|
6396
6500
|
this.buffer.push([tag, this.id, data, Date.now()]);
|
|
6397
6501
|
};
|
|
6502
|
+
this.traceOnce = (key, tag, data) => {
|
|
6503
|
+
if (this.keys?.has(key))
|
|
6504
|
+
return;
|
|
6505
|
+
this.trace(tag, data);
|
|
6506
|
+
(this.keys ?? (this.keys = new Map())).set(key, true);
|
|
6507
|
+
};
|
|
6508
|
+
this.resetTrace = (key) => {
|
|
6509
|
+
this.keys?.delete(key);
|
|
6510
|
+
};
|
|
6398
6511
|
this.take = () => {
|
|
6399
6512
|
const snapshot = this.buffer;
|
|
6400
6513
|
this.buffer = [];
|
|
@@ -6407,6 +6520,7 @@ class Tracer {
|
|
|
6407
6520
|
};
|
|
6408
6521
|
this.dispose = () => {
|
|
6409
6522
|
this.buffer = [];
|
|
6523
|
+
this.keys?.clear();
|
|
6410
6524
|
};
|
|
6411
6525
|
this.id = id;
|
|
6412
6526
|
}
|
|
@@ -6420,7 +6534,7 @@ class BasePeerConnection {
|
|
|
6420
6534
|
/**
|
|
6421
6535
|
* Constructs a new `BasePeerConnection` instance.
|
|
6422
6536
|
*/
|
|
6423
|
-
constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded,
|
|
6537
|
+
constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, tag, enableTracing, iceRestartDelay = 2500, }) {
|
|
6424
6538
|
this.isIceRestarting = false;
|
|
6425
6539
|
this.isDisposed = false;
|
|
6426
6540
|
this.trackIdToTrackType = new Map();
|
|
@@ -6644,13 +6758,12 @@ class BasePeerConnection {
|
|
|
6644
6758
|
this.onReconnectionNeeded = onReconnectionNeeded;
|
|
6645
6759
|
this.logger = getLogger([
|
|
6646
6760
|
peerType === PeerType.SUBSCRIBER ? 'Subscriber' : 'Publisher',
|
|
6647
|
-
|
|
6761
|
+
tag,
|
|
6648
6762
|
]);
|
|
6649
6763
|
this.pc = this.createPeerConnection(connectionConfig);
|
|
6650
6764
|
this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType);
|
|
6651
6765
|
if (enableTracing) {
|
|
6652
|
-
|
|
6653
|
-
this.tracer = new Tracer(tag);
|
|
6766
|
+
this.tracer = new Tracer(`${tag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}`);
|
|
6654
6767
|
this.tracer.trace('create', {
|
|
6655
6768
|
url: sfuClient.edgeName,
|
|
6656
6769
|
...connectionConfig,
|
|
@@ -6871,9 +6984,8 @@ const computeVideoLayers = (videoTrack, publishOption) => {
|
|
|
6871
6984
|
if (isAudioTrackType(publishOption.trackType))
|
|
6872
6985
|
return;
|
|
6873
6986
|
const optimalVideoLayers = [];
|
|
6874
|
-
const
|
|
6875
|
-
const { width =
|
|
6876
|
-
const { bitrate, codec, fps, maxSpatialLayers = 3, maxTemporalLayers = 3, videoDimension = { width: 1280, height: 720 }, useSingleLayer, } = publishOption;
|
|
6987
|
+
const { bitrate, codec, fps = 30, maxSpatialLayers = 3, maxTemporalLayers = 3, videoDimension = { width: 1280, height: 720 }, useSingleLayer, } = publishOption;
|
|
6988
|
+
const { width = videoDimension.width, height = videoDimension.height } = videoTrack.getSettings();
|
|
6877
6989
|
const maxBitrate = getComputedMaxBitrate(videoDimension, width, height, bitrate);
|
|
6878
6990
|
let downscaleFactor = 1;
|
|
6879
6991
|
let bitrateFactor = 1;
|
|
@@ -6905,7 +7017,7 @@ const computeVideoLayers = (videoTrack, publishOption) => {
|
|
|
6905
7017
|
}
|
|
6906
7018
|
// for simplicity, we start with all layers enabled, then this function
|
|
6907
7019
|
// will clear/reassign the layers that are not needed
|
|
6908
|
-
return withSimulcastConstraints(
|
|
7020
|
+
return withSimulcastConstraints(width, height, optimalVideoLayers, useSingleLayer);
|
|
6909
7021
|
};
|
|
6910
7022
|
/**
|
|
6911
7023
|
* Computes the maximum bitrate for a given resolution.
|
|
@@ -6939,9 +7051,9 @@ const getComputedMaxBitrate = (targetResolution, currentWidth, currentHeight, bi
|
|
|
6939
7051
|
*
|
|
6940
7052
|
* https://chromium.googlesource.com/external/webrtc/+/refs/heads/main/media/engine/simulcast.cc#90
|
|
6941
7053
|
*/
|
|
6942
|
-
const withSimulcastConstraints = (
|
|
7054
|
+
const withSimulcastConstraints = (width, height, optimalVideoLayers, useSingleLayer) => {
|
|
6943
7055
|
let layers;
|
|
6944
|
-
const size = Math.max(
|
|
7056
|
+
const size = Math.max(width, height);
|
|
6945
7057
|
if (size <= 320) {
|
|
6946
7058
|
// provide only one layer 320x240 (f), the one with the highest quality
|
|
6947
7059
|
layers = optimalVideoLayers.filter((layer) => layer.rid === 'f');
|
|
@@ -7522,8 +7634,8 @@ class Subscriber extends BasePeerConnection {
|
|
|
7522
7634
|
}
|
|
7523
7635
|
|
|
7524
7636
|
const createWebSocketSignalChannel = (opts) => {
|
|
7525
|
-
const { endpoint, onMessage,
|
|
7526
|
-
const logger = getLogger(['SfuClientWS',
|
|
7637
|
+
const { endpoint, onMessage, tag } = opts;
|
|
7638
|
+
const logger = getLogger(['SfuClientWS', tag]);
|
|
7527
7639
|
logger('debug', 'Creating signaling WS channel:', endpoint);
|
|
7528
7640
|
const ws = new WebSocket(endpoint);
|
|
7529
7641
|
ws.binaryType = 'arraybuffer'; // do we need this?
|
|
@@ -7625,9 +7737,9 @@ const uninitialized = Symbol('uninitialized');
|
|
|
7625
7737
|
*/
|
|
7626
7738
|
function lazy(factory) {
|
|
7627
7739
|
let value = uninitialized;
|
|
7628
|
-
return () => {
|
|
7740
|
+
return (...args) => {
|
|
7629
7741
|
if (value === uninitialized) {
|
|
7630
|
-
value = factory();
|
|
7742
|
+
value = factory(...args);
|
|
7631
7743
|
}
|
|
7632
7744
|
return value;
|
|
7633
7745
|
};
|
|
@@ -7773,7 +7885,7 @@ class StreamSfuClient {
|
|
|
7773
7885
|
/**
|
|
7774
7886
|
* Constructs a new SFU client.
|
|
7775
7887
|
*/
|
|
7776
|
-
constructor({ dispatcher, credentials, sessionId,
|
|
7888
|
+
constructor({ dispatcher, credentials, sessionId, cid, tag, joinResponseTimeout = 5000, onSignalClose, streamClient, enableTracing, }) {
|
|
7777
7889
|
/**
|
|
7778
7890
|
* A buffer for ICE Candidates that are received before
|
|
7779
7891
|
* the Publisher and Subscriber Peer Connections are ready to handle them.
|
|
@@ -7803,7 +7915,7 @@ class StreamSfuClient {
|
|
|
7803
7915
|
* A controller to abort the current requests.
|
|
7804
7916
|
*/
|
|
7805
7917
|
this.abortController = new AbortController();
|
|
7806
|
-
this.createWebSocket = () => {
|
|
7918
|
+
this.createWebSocket = (params) => {
|
|
7807
7919
|
const eventsToTrace = {
|
|
7808
7920
|
callEnded: true,
|
|
7809
7921
|
changePublishQuality: true,
|
|
@@ -7811,10 +7923,11 @@ class StreamSfuClient {
|
|
|
7811
7923
|
connectionQualityChanged: true,
|
|
7812
7924
|
error: true,
|
|
7813
7925
|
goAway: true,
|
|
7926
|
+
inboundStateNotification: true,
|
|
7814
7927
|
};
|
|
7815
7928
|
this.signalWs = createWebSocketSignalChannel({
|
|
7816
|
-
|
|
7817
|
-
endpoint: `${this.credentials.server.ws_endpoint}
|
|
7929
|
+
tag: this.tag,
|
|
7930
|
+
endpoint: `${this.credentials.server.ws_endpoint}?${new URLSearchParams(params).toString()}`,
|
|
7818
7931
|
onMessage: (message) => {
|
|
7819
7932
|
this.lastMessageTimestamp = new Date();
|
|
7820
7933
|
this.scheduleConnectionCheck();
|
|
@@ -7822,7 +7935,7 @@ class StreamSfuClient {
|
|
|
7822
7935
|
if (eventsToTrace[eventKind]) {
|
|
7823
7936
|
this.tracer?.trace(eventKind, message);
|
|
7824
7937
|
}
|
|
7825
|
-
this.dispatcher.dispatch(message, this.
|
|
7938
|
+
this.dispatcher.dispatch(message, this.tag);
|
|
7826
7939
|
},
|
|
7827
7940
|
});
|
|
7828
7941
|
this.signalReady = makeSafePromise(Promise.race([
|
|
@@ -7937,7 +8050,7 @@ class StreamSfuClient {
|
|
|
7937
8050
|
});
|
|
7938
8051
|
this.migrateAwayTimeout = setTimeout(() => {
|
|
7939
8052
|
unsubscribe();
|
|
7940
|
-
task.reject(new Error(`Migration (${this.
|
|
8053
|
+
task.reject(new Error(`Migration (${this.tag}) failed to complete in ${timeout}ms`));
|
|
7941
8054
|
}, timeout);
|
|
7942
8055
|
return task.promise;
|
|
7943
8056
|
};
|
|
@@ -8037,10 +8150,10 @@ class StreamSfuClient {
|
|
|
8037
8150
|
const { server, token } = credentials;
|
|
8038
8151
|
this.edgeName = server.edge_name;
|
|
8039
8152
|
this.joinResponseTimeout = joinResponseTimeout;
|
|
8040
|
-
this.
|
|
8041
|
-
this.logger = getLogger(['SfuClient',
|
|
8153
|
+
this.tag = tag;
|
|
8154
|
+
this.logger = getLogger(['SfuClient', tag]);
|
|
8042
8155
|
this.tracer = enableTracing
|
|
8043
|
-
? new Tracer(`${
|
|
8156
|
+
? new Tracer(`${tag}-${this.edgeName}`)
|
|
8044
8157
|
: undefined;
|
|
8045
8158
|
this.rpc = createSignalClient({
|
|
8046
8159
|
baseUrl: server.url,
|
|
@@ -8069,7 +8182,13 @@ class StreamSfuClient {
|
|
|
8069
8182
|
this.networkAvailableTask?.resolve();
|
|
8070
8183
|
}
|
|
8071
8184
|
});
|
|
8072
|
-
this.createWebSocket(
|
|
8185
|
+
this.createWebSocket({
|
|
8186
|
+
attempt: tag,
|
|
8187
|
+
user_id: streamClient.user?.id || '',
|
|
8188
|
+
api_key: streamClient.key,
|
|
8189
|
+
user_session_id: this.sessionId,
|
|
8190
|
+
cid,
|
|
8191
|
+
});
|
|
8073
8192
|
}
|
|
8074
8193
|
get isHealthy() {
|
|
8075
8194
|
return (this.signalWs.readyState === WebSocket.OPEN &&
|
|
@@ -8229,6 +8348,36 @@ const watchCallGrantsUpdated = (state) => {
|
|
|
8229
8348
|
};
|
|
8230
8349
|
};
|
|
8231
8350
|
|
|
8351
|
+
/**
|
|
8352
|
+
* Adds unique values to an array.
|
|
8353
|
+
*
|
|
8354
|
+
* @param arr the array to add to.
|
|
8355
|
+
* @param values the values to add.
|
|
8356
|
+
*/
|
|
8357
|
+
const pushToIfMissing = (arr, ...values) => {
|
|
8358
|
+
for (const v of values) {
|
|
8359
|
+
if (!arr.includes(v)) {
|
|
8360
|
+
arr.push(v);
|
|
8361
|
+
}
|
|
8362
|
+
}
|
|
8363
|
+
return arr;
|
|
8364
|
+
};
|
|
8365
|
+
/**
|
|
8366
|
+
* Removes values from an array if they are present.
|
|
8367
|
+
*
|
|
8368
|
+
* @param arr the array to remove from.
|
|
8369
|
+
* @param values the values to remove.
|
|
8370
|
+
*/
|
|
8371
|
+
const removeFromIfPresent = (arr, ...values) => {
|
|
8372
|
+
for (const v of values) {
|
|
8373
|
+
const index = arr.indexOf(v);
|
|
8374
|
+
if (index !== -1) {
|
|
8375
|
+
arr.splice(index, 1);
|
|
8376
|
+
}
|
|
8377
|
+
}
|
|
8378
|
+
return arr;
|
|
8379
|
+
};
|
|
8380
|
+
|
|
8232
8381
|
const watchConnectionQualityChanged = (dispatcher, state) => {
|
|
8233
8382
|
return dispatcher.on('connectionQualityChanged', (e) => {
|
|
8234
8383
|
const { connectionQualityUpdates } = e;
|
|
@@ -8295,6 +8444,29 @@ const watchPinsUpdated = (state) => {
|
|
|
8295
8444
|
state.setServerSidePins(pins);
|
|
8296
8445
|
};
|
|
8297
8446
|
};
|
|
8447
|
+
/**
|
|
8448
|
+
* Watches for inbound state notifications and updates the paused tracks
|
|
8449
|
+
*
|
|
8450
|
+
* @param state the call state to update.
|
|
8451
|
+
*/
|
|
8452
|
+
const watchInboundStateNotification = (state) => {
|
|
8453
|
+
return function onInboundStateNotification(e) {
|
|
8454
|
+
const { inboundVideoStates } = e;
|
|
8455
|
+
const current = state.getParticipantLookupBySessionId();
|
|
8456
|
+
const patches = {};
|
|
8457
|
+
for (const { sessionId, trackType, paused } of inboundVideoStates) {
|
|
8458
|
+
const pausedTracks = [...(current[sessionId]?.pausedTracks ?? [])];
|
|
8459
|
+
if (paused) {
|
|
8460
|
+
pushToIfMissing(pausedTracks, trackType);
|
|
8461
|
+
}
|
|
8462
|
+
else {
|
|
8463
|
+
removeFromIfPresent(pausedTracks, trackType);
|
|
8464
|
+
}
|
|
8465
|
+
patches[sessionId] = { pausedTracks };
|
|
8466
|
+
}
|
|
8467
|
+
state.updateParticipants(patches);
|
|
8468
|
+
};
|
|
8469
|
+
};
|
|
8298
8470
|
|
|
8299
8471
|
/**
|
|
8300
8472
|
* An event handler that handles soft mutes.
|
|
@@ -8331,21 +8503,6 @@ const handleRemoteSoftMute = (call) => {
|
|
|
8331
8503
|
});
|
|
8332
8504
|
};
|
|
8333
8505
|
|
|
8334
|
-
/**
|
|
8335
|
-
* Adds unique values to an array.
|
|
8336
|
-
*
|
|
8337
|
-
* @param arr the array to add to.
|
|
8338
|
-
* @param values the values to add.
|
|
8339
|
-
*/
|
|
8340
|
-
const pushToIfMissing = (arr, ...values) => {
|
|
8341
|
-
for (const v of values) {
|
|
8342
|
-
if (!arr.includes(v)) {
|
|
8343
|
-
arr.push(v);
|
|
8344
|
-
}
|
|
8345
|
-
}
|
|
8346
|
-
return arr;
|
|
8347
|
-
};
|
|
8348
|
-
|
|
8349
8506
|
/**
|
|
8350
8507
|
* An event responder which handles the `participantJoined` event.
|
|
8351
8508
|
*/
|
|
@@ -8427,11 +8584,14 @@ const watchTrackUnpublished = (state) => {
|
|
|
8427
8584
|
if (e.participant) {
|
|
8428
8585
|
const orphanedTracks = reconcileOrphanedTracks(state, e.participant);
|
|
8429
8586
|
const participant = Object.assign(e.participant, orphanedTracks);
|
|
8430
|
-
state.updateOrAddParticipant(sessionId, participant)
|
|
8587
|
+
state.updateOrAddParticipant(sessionId, participant, (p) => ({
|
|
8588
|
+
pausedTracks: p.pausedTracks?.filter((t) => t !== type),
|
|
8589
|
+
}));
|
|
8431
8590
|
}
|
|
8432
8591
|
else {
|
|
8433
8592
|
state.updateParticipant(sessionId, (p) => ({
|
|
8434
8593
|
publishedTracks: p.publishedTracks.filter((t) => t !== type),
|
|
8594
|
+
pausedTracks: p.pausedTracks?.filter((t) => t !== type),
|
|
8435
8595
|
}));
|
|
8436
8596
|
}
|
|
8437
8597
|
};
|
|
@@ -8523,6 +8683,7 @@ const registerEventHandlers = (call, dispatcher) => {
|
|
|
8523
8683
|
watchDominantSpeakerChanged(dispatcher, state),
|
|
8524
8684
|
call.on('callGrantsUpdated', watchCallGrantsUpdated(state)),
|
|
8525
8685
|
call.on('pinsUpdated', watchPinsUpdated(state)),
|
|
8686
|
+
call.on('inboundStateNotification', watchInboundStateNotification(state)),
|
|
8526
8687
|
handleRemoteSoftMute(call),
|
|
8527
8688
|
];
|
|
8528
8689
|
if (call.ringing) {
|
|
@@ -9392,8 +9553,9 @@ function canQueryPermissions() {
|
|
|
9392
9553
|
*
|
|
9393
9554
|
* @param permission a BrowserPermission instance.
|
|
9394
9555
|
* @param kind the kind of devices to enumerate.
|
|
9556
|
+
* @param tracer the tracer to use for tracing the device enumeration.
|
|
9395
9557
|
*/
|
|
9396
|
-
const getDevices = (permission, kind) => {
|
|
9558
|
+
const getDevices = (permission, kind, tracer) => {
|
|
9397
9559
|
return from((async () => {
|
|
9398
9560
|
let devices = await navigator.mediaDevices.enumerateDevices();
|
|
9399
9561
|
// for privacy reasons, most browsers don't give you device labels
|
|
@@ -9402,6 +9564,7 @@ const getDevices = (permission, kind) => {
|
|
|
9402
9564
|
if (shouldPromptForBrowserPermission && (await permission.prompt())) {
|
|
9403
9565
|
devices = await navigator.mediaDevices.enumerateDevices();
|
|
9404
9566
|
}
|
|
9567
|
+
tracer?.traceOnce('device-enumeration', 'navigator.mediaDevices.enumerateDevices', devices);
|
|
9405
9568
|
return devices.filter((device) => device.kind === kind &&
|
|
9406
9569
|
device.label !== '' &&
|
|
9407
9570
|
device.deviceId !== 'default');
|
|
@@ -9452,12 +9615,12 @@ const getVideoBrowserPermission = lazy(() => new BrowserPermission({
|
|
|
9452
9615
|
constraints: videoDeviceConstraints,
|
|
9453
9616
|
queryName: 'camera',
|
|
9454
9617
|
}));
|
|
9455
|
-
const getDeviceChangeObserver = lazy(() => {
|
|
9618
|
+
const getDeviceChangeObserver = lazy((tracer) => {
|
|
9456
9619
|
// 'addEventListener' is not available in React Native, returning
|
|
9457
9620
|
// an observable that will never fire
|
|
9458
9621
|
if (!navigator.mediaDevices.addEventListener)
|
|
9459
9622
|
return from([]);
|
|
9460
|
-
return fromEvent(navigator.mediaDevices, 'devicechange').pipe(map(() => undefined), debounceTime(500));
|
|
9623
|
+
return fromEvent(navigator.mediaDevices, 'devicechange').pipe(tap(() => tracer?.resetTrace('device-enumeration')), map(() => undefined), debounceTime(500));
|
|
9461
9624
|
});
|
|
9462
9625
|
/**
|
|
9463
9626
|
* Prompts the user for a permission to use audio devices (if not already granted
|
|
@@ -9465,8 +9628,8 @@ const getDeviceChangeObserver = lazy(() => {
|
|
|
9465
9628
|
* if devices are added/removed the list is updated, and if the permission is revoked,
|
|
9466
9629
|
* the observable errors.
|
|
9467
9630
|
*/
|
|
9468
|
-
const getAudioDevices = lazy(() => {
|
|
9469
|
-
return merge(getDeviceChangeObserver(), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audioinput')), shareReplay(1));
|
|
9631
|
+
const getAudioDevices = lazy((tracer) => {
|
|
9632
|
+
return merge(getDeviceChangeObserver(tracer), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audioinput', tracer)), shareReplay(1));
|
|
9470
9633
|
});
|
|
9471
9634
|
/**
|
|
9472
9635
|
* Prompts the user for a permission to use video devices (if not already granted
|
|
@@ -9474,8 +9637,8 @@ const getAudioDevices = lazy(() => {
|
|
|
9474
9637
|
* if devices are added/removed the list is updated, and if the permission is revoked,
|
|
9475
9638
|
* the observable errors.
|
|
9476
9639
|
*/
|
|
9477
|
-
const getVideoDevices = lazy(() => {
|
|
9478
|
-
return merge(getDeviceChangeObserver(), getVideoBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getVideoBrowserPermission(), 'videoinput')), shareReplay(1));
|
|
9640
|
+
const getVideoDevices = lazy((tracer) => {
|
|
9641
|
+
return merge(getDeviceChangeObserver(tracer), getVideoBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getVideoBrowserPermission(), 'videoinput', tracer)), shareReplay(1));
|
|
9479
9642
|
});
|
|
9480
9643
|
/**
|
|
9481
9644
|
* Prompts the user for a permission to use video devices (if not already granted
|
|
@@ -9483,8 +9646,8 @@ const getVideoDevices = lazy(() => {
|
|
|
9483
9646
|
* if devices are added/removed the list is updated, and if the permission is revoked,
|
|
9484
9647
|
* the observable errors.
|
|
9485
9648
|
*/
|
|
9486
|
-
const getAudioOutputDevices = lazy(() => {
|
|
9487
|
-
return merge(getDeviceChangeObserver(), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audiooutput')), shareReplay(1));
|
|
9649
|
+
const getAudioOutputDevices = lazy((tracer) => {
|
|
9650
|
+
return merge(getDeviceChangeObserver(tracer), getAudioBrowserPermission().asObservable()).pipe(startWith(undefined), concatMap(() => getDevices(getAudioBrowserPermission(), 'audiooutput', tracer)), shareReplay(1));
|
|
9488
9651
|
});
|
|
9489
9652
|
let getUserMediaExecId = 0;
|
|
9490
9653
|
const getStream = async (constraints, tracer) => {
|
|
@@ -9856,6 +10019,7 @@ class InputMediaDeviceManager {
|
|
|
9856
10019
|
entry.stop?.();
|
|
9857
10020
|
this.filters = this.filters.filter((f) => f !== entry);
|
|
9858
10021
|
await this.applySettingsToStream();
|
|
10022
|
+
this.call.tracer.trace(`unregisterFilter.${TrackType[this.trackType]}`, null);
|
|
9859
10023
|
}),
|
|
9860
10024
|
};
|
|
9861
10025
|
}
|
|
@@ -9875,7 +10039,7 @@ class InputMediaDeviceManager {
|
|
|
9875
10039
|
*/
|
|
9876
10040
|
async select(deviceId) {
|
|
9877
10041
|
if (isReactNative()) {
|
|
9878
|
-
throw new Error('This method is not supported in React Native.
|
|
10042
|
+
throw new Error('This method is not supported in React Native.');
|
|
9879
10043
|
}
|
|
9880
10044
|
const prevDeviceId = this.state.selectedDevice;
|
|
9881
10045
|
if (deviceId === prevDeviceId) {
|
|
@@ -10442,7 +10606,7 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
10442
10606
|
}
|
|
10443
10607
|
}
|
|
10444
10608
|
getDevices() {
|
|
10445
|
-
return getVideoDevices();
|
|
10609
|
+
return getVideoDevices(this.call.tracer);
|
|
10446
10610
|
}
|
|
10447
10611
|
getStream(constraints) {
|
|
10448
10612
|
constraints.width = this.targetResolution.width;
|
|
@@ -10849,6 +11013,7 @@ class MicrophoneManager extends InputMediaDeviceManager {
|
|
|
10849
11013
|
.catch((err) => {
|
|
10850
11014
|
this.logger('warn', 'Failed to unregister noise cancellation', err);
|
|
10851
11015
|
});
|
|
11016
|
+
this.call.tracer.trace('noiseCancellation.disabled', true);
|
|
10852
11017
|
await this.call.notifyNoiseCancellationStopped();
|
|
10853
11018
|
}
|
|
10854
11019
|
/**
|
|
@@ -10893,7 +11058,7 @@ class MicrophoneManager extends InputMediaDeviceManager {
|
|
|
10893
11058
|
}
|
|
10894
11059
|
}
|
|
10895
11060
|
getDevices() {
|
|
10896
|
-
return getAudioDevices();
|
|
11061
|
+
return getAudioDevices(this.call.tracer);
|
|
10897
11062
|
}
|
|
10898
11063
|
getStream(constraints) {
|
|
10899
11064
|
return getAudioStream(constraints, this.call.tracer);
|
|
@@ -11158,7 +11323,7 @@ class SpeakerManager {
|
|
|
11158
11323
|
if (isReactNative()) {
|
|
11159
11324
|
throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
|
|
11160
11325
|
}
|
|
11161
|
-
return getAudioOutputDevices();
|
|
11326
|
+
return getAudioOutputDevices(this.call.tracer);
|
|
11162
11327
|
}
|
|
11163
11328
|
/**
|
|
11164
11329
|
* Select a device.
|
|
@@ -11256,6 +11421,12 @@ class Call {
|
|
|
11256
11421
|
*/
|
|
11257
11422
|
this.leaveCallHooks = new Set();
|
|
11258
11423
|
this.streamClientEventHandlers = new Map();
|
|
11424
|
+
/**
|
|
11425
|
+
* A list of capabilities that the client supports and are enabled.
|
|
11426
|
+
*/
|
|
11427
|
+
this.clientCapabilities = new Set([
|
|
11428
|
+
ClientCapability.SUBSCRIBER_VIDEO_PAUSE,
|
|
11429
|
+
]);
|
|
11259
11430
|
/**
|
|
11260
11431
|
* Sets up the call instance.
|
|
11261
11432
|
*
|
|
@@ -11664,14 +11835,27 @@ class Call {
|
|
|
11664
11835
|
throw new Error(`Illegal State: call.join() shall be called only once`);
|
|
11665
11836
|
}
|
|
11666
11837
|
this.state.setCallingState(CallingState.JOINING);
|
|
11838
|
+
// we will count the number of join failures per SFU.
|
|
11839
|
+
// once the number of failures reaches 2, we will piggyback on the `migrating_from`
|
|
11840
|
+
// field to force the coordinator to provide us another SFU
|
|
11841
|
+
const sfuJoinFailures = new Map();
|
|
11842
|
+
const joinData = data;
|
|
11667
11843
|
maxJoinRetries = Math.max(maxJoinRetries, 1);
|
|
11668
11844
|
for (let attempt = 0; attempt < maxJoinRetries; attempt++) {
|
|
11669
11845
|
try {
|
|
11670
11846
|
this.logger('trace', `Joining call (${attempt})`, this.cid);
|
|
11671
|
-
|
|
11847
|
+
await this.doJoin(data);
|
|
11848
|
+
delete joinData.migrating_from;
|
|
11849
|
+
break;
|
|
11672
11850
|
}
|
|
11673
11851
|
catch (err) {
|
|
11674
11852
|
this.logger('warn', `Failed to join call (${attempt})`, this.cid);
|
|
11853
|
+
const sfuId = this.credentials?.server.edge_name || '';
|
|
11854
|
+
const failures = (sfuJoinFailures.get(sfuId) || 0) + 1;
|
|
11855
|
+
sfuJoinFailures.set(sfuId, failures);
|
|
11856
|
+
if (failures >= 2) {
|
|
11857
|
+
joinData.migrating_from = sfuId;
|
|
11858
|
+
}
|
|
11675
11859
|
if (attempt === maxJoinRetries - 1) {
|
|
11676
11860
|
// restore the previous call state if the join-flow fails
|
|
11677
11861
|
this.state.setCallingState(callingState);
|
|
@@ -11721,7 +11905,8 @@ class Call {
|
|
|
11721
11905
|
const isWsHealthy = !!previousSfuClient?.isHealthy;
|
|
11722
11906
|
const sfuClient = performingRejoin || performingMigration || !isWsHealthy
|
|
11723
11907
|
? new StreamSfuClient({
|
|
11724
|
-
|
|
11908
|
+
tag: String(this.sfuClientTag++),
|
|
11909
|
+
cid: this.cid,
|
|
11725
11910
|
dispatcher: this.dispatcher,
|
|
11726
11911
|
credentials: this.credentials,
|
|
11727
11912
|
streamClient: this.streamClient,
|
|
@@ -11763,6 +11948,7 @@ class Call {
|
|
|
11763
11948
|
reconnectDetails,
|
|
11764
11949
|
preferredPublishOptions,
|
|
11765
11950
|
preferredSubscribeOptions,
|
|
11951
|
+
capabilities: Array.from(this.clientCapabilities),
|
|
11766
11952
|
});
|
|
11767
11953
|
this.currentPublishOptions = publishOptions;
|
|
11768
11954
|
this.fastReconnectDeadlineSeconds = fastReconnectDeadlineSeconds;
|
|
@@ -11935,7 +12121,7 @@ class Call {
|
|
|
11935
12121
|
dispatcher: this.dispatcher,
|
|
11936
12122
|
state: this.state,
|
|
11937
12123
|
connectionConfig,
|
|
11938
|
-
|
|
12124
|
+
tag: sfuClient.tag,
|
|
11939
12125
|
enableTracing,
|
|
11940
12126
|
onReconnectionNeeded: (kind, reason) => {
|
|
11941
12127
|
this.reconnect(kind, reason).catch((err) => {
|
|
@@ -11957,7 +12143,7 @@ class Call {
|
|
|
11957
12143
|
state: this.state,
|
|
11958
12144
|
connectionConfig,
|
|
11959
12145
|
publishOptions,
|
|
11960
|
-
|
|
12146
|
+
tag: sfuClient.tag,
|
|
11961
12147
|
enableTracing,
|
|
11962
12148
|
onReconnectionNeeded: (kind, reason) => {
|
|
11963
12149
|
this.reconnect(kind, reason).catch((err) => {
|
|
@@ -12475,9 +12661,12 @@ class Call {
|
|
|
12475
12661
|
* @internal
|
|
12476
12662
|
*/
|
|
12477
12663
|
this.notifyTrackMuteState = async (muted, ...trackTypes) => {
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12664
|
+
const key = `muteState.${this.cid}.${trackTypes.join('-')}`;
|
|
12665
|
+
await withoutConcurrency(key, async () => {
|
|
12666
|
+
if (!this.sfuClient)
|
|
12667
|
+
return;
|
|
12668
|
+
await this.sfuClient.updateMuteStates(trackTypes.map((trackType) => ({ trackType, muted })));
|
|
12669
|
+
});
|
|
12481
12670
|
};
|
|
12482
12671
|
/**
|
|
12483
12672
|
* Will enhance the reported stats with additional participant-specific information (`callStatsReport$` state [store variable](./StreamVideoClient.md/#readonlystatestore)).
|
|
@@ -13121,6 +13310,22 @@ class Call {
|
|
|
13121
13310
|
this.setDisconnectionTimeout = (timeoutSeconds) => {
|
|
13122
13311
|
this.disconnectionTimeoutSeconds = timeoutSeconds;
|
|
13123
13312
|
};
|
|
13313
|
+
/**
|
|
13314
|
+
* Enables the provided client capabilities.
|
|
13315
|
+
*/
|
|
13316
|
+
this.enableClientCapabilities = (...capabilities) => {
|
|
13317
|
+
for (const capability of capabilities) {
|
|
13318
|
+
this.clientCapabilities.add(capability);
|
|
13319
|
+
}
|
|
13320
|
+
};
|
|
13321
|
+
/**
|
|
13322
|
+
* Disables the provided client capabilities.
|
|
13323
|
+
*/
|
|
13324
|
+
this.disableClientCapabilities = (...capabilities) => {
|
|
13325
|
+
for (const capability of capabilities) {
|
|
13326
|
+
this.clientCapabilities.delete(capability);
|
|
13327
|
+
}
|
|
13328
|
+
};
|
|
13124
13329
|
this.type = type;
|
|
13125
13330
|
this.id = id;
|
|
13126
13331
|
this.cid = `${type}:${id}`;
|
|
@@ -13286,7 +13491,6 @@ class StableWSConnection {
|
|
|
13286
13491
|
this.onmessage = (wsID, event) => {
|
|
13287
13492
|
if (this.wsID !== wsID)
|
|
13288
13493
|
return;
|
|
13289
|
-
this._log('onmessage() - onmessage callback', { event, wsID });
|
|
13290
13494
|
const data = typeof event.data === 'string'
|
|
13291
13495
|
? JSON.parse(event.data)
|
|
13292
13496
|
: null;
|
|
@@ -13372,7 +13576,6 @@ class StableWSConnection {
|
|
|
13372
13576
|
this.totalFailures += 1;
|
|
13373
13577
|
this._setHealth(false);
|
|
13374
13578
|
this.isConnecting = false;
|
|
13375
|
-
this.rejectConnectionOpen?.(new Error(`WebSocket error: ${event}`));
|
|
13376
13579
|
this._log(`onerror() - WS connection resulted into error`, { event });
|
|
13377
13580
|
this._reconnect();
|
|
13378
13581
|
};
|
|
@@ -14300,7 +14503,7 @@ class StreamClient {
|
|
|
14300
14503
|
this.getUserAgent = () => {
|
|
14301
14504
|
if (!this.cachedUserAgent) {
|
|
14302
14505
|
const { clientAppIdentifier = {} } = this.options;
|
|
14303
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
14506
|
+
const { sdkName = 'js', sdkVersion = "1.27.1", ...extras } = clientAppIdentifier;
|
|
14304
14507
|
this.cachedUserAgent = [
|
|
14305
14508
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
14306
14509
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -14853,5 +15056,5 @@ class StreamVideoClient {
|
|
|
14853
15056
|
}
|
|
14854
15057
|
StreamVideoClient._instances = new Map();
|
|
14855
15058
|
|
|
14856
|
-
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, InputMediaDeviceManager, InputMediaDeviceManagerState, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RNSpeechDetector, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getLogLevel, getLogger, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio, hasScreenShare, hasScreenShareAudio, hasVideo, isPinned, livestreamOrAudioRoomSortPreset, logLevels, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setLogLevel, setLogger, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking };
|
|
15059
|
+
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, InputMediaDeviceManager, InputMediaDeviceManagerState, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RNSpeechDetector, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getLogLevel, getLogger, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, isPinned, livestreamOrAudioRoomSortPreset, logLevels, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setLogLevel, setLogger, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking };
|
|
14857
15060
|
//# sourceMappingURL=index.es.js.map
|