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