@stream-io/video-client 0.0.1-alpha.81 → 0.0.1-alpha.83
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/dist/index.browser.es.js +501 -290
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +499 -288
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +501 -290
- package/dist/index.es.js.map +1 -1
- package/dist/src/StreamSfuClient.d.ts +26 -4
- package/dist/src/StreamVideoClient.d.ts +2 -2
- package/dist/src/coordinator/connection/client.d.ts +4 -4
- package/dist/src/coordinator/connection/types.d.ts +20 -9
- package/dist/src/events/call-permissions.d.ts +3 -3
- package/dist/src/events/call.d.ts +5 -5
- package/dist/src/events/moderation.d.ts +3 -3
- package/dist/src/events/reactions.d.ts +2 -2
- package/dist/src/events/recording.d.ts +3 -3
- package/dist/src/rtc/Call.d.ts +10 -8
- package/dist/src/rtc/Dispatcher.d.ts +2 -0
- package/dist/src/rtc/publisher.d.ts +7 -2
- package/dist/src/rtc/signal.d.ts +1 -1
- package/dist/src/rtc/subscriber.d.ts +3 -1
- package/dist/src/store/CallState.d.ts +43 -0
- package/package.json +1 -1
- package/src/StreamSfuClient.ts +70 -27
- package/src/StreamVideoClient.ts +7 -32
- package/src/coordinator/connection/client.ts +6 -6
- package/src/coordinator/connection/connection.ts +2 -1
- package/src/coordinator/connection/connection_fallback.ts +2 -2
- package/src/coordinator/connection/types.ts +50 -11
- package/src/coordinator/connection/utils.ts +3 -3
- package/src/events/call-permissions.ts +9 -6
- package/src/events/call.ts +17 -10
- package/src/events/moderation.ts +9 -3
- package/src/events/reactions.ts +5 -2
- package/src/events/recording.ts +9 -6
- package/src/rtc/Call.ts +193 -52
- package/src/rtc/Dispatcher.ts +24 -0
- package/src/rtc/publisher.ts +39 -25
- package/src/rtc/signal.ts +23 -23
- package/src/rtc/subscriber.ts +39 -23
- package/src/store/CallState.ts +54 -0
- package/dist/src/coordinator/connection/events.d.ts +0 -12
- package/src/coordinator/connection/events.ts +0 -15
package/dist/index.browser.es.js
CHANGED
|
@@ -3,7 +3,7 @@ import { MessageType, isJsonObject, typeofJsonValue, reflectionMergePartial, Unk
|
|
|
3
3
|
import { TwirpFetchTransport } from '@protobuf-ts/twirp-transport';
|
|
4
4
|
import { ServiceType, stackIntercept } from '@protobuf-ts/runtime-rpc';
|
|
5
5
|
import { v4 } from 'uuid';
|
|
6
|
-
import { ReplaySubject, BehaviorSubject, Subject, takeWhile,
|
|
6
|
+
import { ReplaySubject, BehaviorSubject, Subject, takeWhile, debounceTime, startWith, pairwise, Observable, concatMap, from, shareReplay, merge, map as map$2, combineLatest, filter, firstValueFrom } from 'rxjs';
|
|
7
7
|
import { take, combineLatestWith, map as map$1, distinctUntilChanged } from 'rxjs/operators';
|
|
8
8
|
import axios, { AxiosHeaders } from 'axios';
|
|
9
9
|
import WebSocket$1 from 'isomorphic-ws';
|
|
@@ -3821,28 +3821,27 @@ const createSignalClient = (options) => {
|
|
|
3821
3821
|
};
|
|
3822
3822
|
|
|
3823
3823
|
const createWebSocketSignalChannel = (opts) => {
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
console.warn('Signalling channel is closed', e);
|
|
3836
|
-
});
|
|
3837
|
-
if (onMessage) {
|
|
3838
|
-
ws.addEventListener('message', (e) => {
|
|
3839
|
-
const message = e.data instanceof ArrayBuffer
|
|
3840
|
-
? SfuEvent.fromBinary(new Uint8Array(e.data))
|
|
3841
|
-
: SfuEvent.fromJsonString(e.data);
|
|
3842
|
-
onMessage(message);
|
|
3843
|
-
});
|
|
3844
|
-
}
|
|
3824
|
+
const { endpoint, onMessage } = opts;
|
|
3825
|
+
const ws = new WebSocket(endpoint);
|
|
3826
|
+
ws.binaryType = 'arraybuffer'; // do we need this?
|
|
3827
|
+
ws.addEventListener('error', (e) => {
|
|
3828
|
+
console.error('Error', e);
|
|
3829
|
+
});
|
|
3830
|
+
ws.addEventListener('close', (e) => {
|
|
3831
|
+
console.warn('Signalling channel is closed', e);
|
|
3832
|
+
});
|
|
3833
|
+
ws.addEventListener('open', (e) => {
|
|
3834
|
+
console.log('Signalling channel is open', e);
|
|
3845
3835
|
});
|
|
3836
|
+
if (onMessage) {
|
|
3837
|
+
ws.addEventListener('message', (e) => {
|
|
3838
|
+
const message = e.data instanceof ArrayBuffer
|
|
3839
|
+
? SfuEvent.fromBinary(new Uint8Array(e.data))
|
|
3840
|
+
: SfuEvent.fromJsonString(e.data);
|
|
3841
|
+
onMessage(message);
|
|
3842
|
+
});
|
|
3843
|
+
}
|
|
3844
|
+
return ws;
|
|
3846
3845
|
};
|
|
3847
3846
|
|
|
3848
3847
|
/**
|
|
@@ -3891,17 +3890,23 @@ const toURL = (url) => {
|
|
|
3891
3890
|
return null;
|
|
3892
3891
|
}
|
|
3893
3892
|
};
|
|
3893
|
+
/**
|
|
3894
|
+
* The client used for exchanging information with the SFU.
|
|
3895
|
+
*/
|
|
3894
3896
|
class StreamSfuClient {
|
|
3895
3897
|
constructor(dispatcher, url, token) {
|
|
3898
|
+
/**
|
|
3899
|
+
* A buffer for ICE Candidates that are received before
|
|
3900
|
+
* the PeerConnections are ready to handle them.
|
|
3901
|
+
*/
|
|
3896
3902
|
this.iceTrickleBuffer = new IceTrickleBuffer();
|
|
3897
|
-
this.
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
});
|
|
3903
|
+
this.pingIntervalInMs = 25 * 1000;
|
|
3904
|
+
this.unhealthyTimeoutInMs = this.pingIntervalInMs + 5 * 1000;
|
|
3905
|
+
this.close = (code = 1000) => {
|
|
3906
|
+
this.signalWs.close(code, 'Requested signal connection close');
|
|
3907
|
+
this.unsubscribeIceTrickle();
|
|
3908
|
+
clearInterval(this.keepAliveInterval);
|
|
3909
|
+
clearTimeout(this.connectionCheckTimeout);
|
|
3905
3910
|
};
|
|
3906
3911
|
this.updateSubscriptions = (subscriptions) => __awaiter(this, void 0, void 0, function* () {
|
|
3907
3912
|
return this.rpc.updateSubscriptions({
|
|
@@ -3945,7 +3950,35 @@ class StreamSfuClient {
|
|
|
3945
3950
|
signal.send(SfuRequest.toBinary(message));
|
|
3946
3951
|
});
|
|
3947
3952
|
};
|
|
3948
|
-
this.
|
|
3953
|
+
this.keepAlive = () => {
|
|
3954
|
+
if (this.keepAliveInterval) {
|
|
3955
|
+
clearInterval(this.keepAliveInterval);
|
|
3956
|
+
}
|
|
3957
|
+
this.keepAliveInterval = setInterval(() => {
|
|
3958
|
+
console.log('Sending healthCheckRequest to SFU');
|
|
3959
|
+
const message = SfuRequest.create({
|
|
3960
|
+
requestPayload: {
|
|
3961
|
+
oneofKind: 'healthCheckRequest',
|
|
3962
|
+
healthCheckRequest: {},
|
|
3963
|
+
},
|
|
3964
|
+
});
|
|
3965
|
+
void this.send(message);
|
|
3966
|
+
}, this.pingIntervalInMs);
|
|
3967
|
+
};
|
|
3968
|
+
this.scheduleConnectionCheck = () => {
|
|
3969
|
+
if (this.connectionCheckTimeout) {
|
|
3970
|
+
clearTimeout(this.connectionCheckTimeout);
|
|
3971
|
+
}
|
|
3972
|
+
this.connectionCheckTimeout = setTimeout(() => {
|
|
3973
|
+
if (this.lastMessageTimestamp) {
|
|
3974
|
+
const timeSinceLastMessage = new Date().getTime() - this.lastMessageTimestamp.getTime();
|
|
3975
|
+
if (timeSinceLastMessage > this.unhealthyTimeoutInMs) {
|
|
3976
|
+
console.log('SFU connection unhealthy, closing');
|
|
3977
|
+
this.close(4001);
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
}, this.unhealthyTimeoutInMs);
|
|
3981
|
+
};
|
|
3949
3982
|
this.sessionId = v4();
|
|
3950
3983
|
this.token = token;
|
|
3951
3984
|
this.rpc = createSignalClient({
|
|
@@ -3972,36 +4005,25 @@ class StreamSfuClient {
|
|
|
3972
4005
|
// connection is established. In that case, those events (ICE candidates)
|
|
3973
4006
|
// need to be buffered and later added to the appropriate PeerConnection
|
|
3974
4007
|
// once the remoteDescription is known and set.
|
|
3975
|
-
this.dispatcher.on('iceTrickle', (e) => {
|
|
4008
|
+
this.unsubscribeIceTrickle = dispatcher.on('iceTrickle', (e) => {
|
|
3976
4009
|
if (e.eventPayload.oneofKind !== 'iceTrickle')
|
|
3977
4010
|
return;
|
|
3978
4011
|
const { iceTrickle } = e.eventPayload;
|
|
3979
4012
|
this.iceTrickleBuffer.push(iceTrickle);
|
|
3980
4013
|
});
|
|
3981
|
-
this.
|
|
4014
|
+
this.signalWs = createWebSocketSignalChannel({
|
|
3982
4015
|
endpoint: wsEndpoint,
|
|
3983
4016
|
onMessage: (message) => {
|
|
3984
|
-
this.
|
|
4017
|
+
this.lastMessageTimestamp = new Date();
|
|
4018
|
+
this.scheduleConnectionCheck();
|
|
4019
|
+
dispatcher.dispatch(message);
|
|
3985
4020
|
},
|
|
3986
4021
|
});
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
console.log('Registering healthcheck for SFU');
|
|
3993
|
-
this.keepAliveInterval = setInterval(() => {
|
|
3994
|
-
const message = SfuRequest.create({
|
|
3995
|
-
requestPayload: {
|
|
3996
|
-
oneofKind: 'healthCheckRequest',
|
|
3997
|
-
healthCheckRequest: {
|
|
3998
|
-
sessionId: this.sessionId,
|
|
3999
|
-
},
|
|
4000
|
-
},
|
|
4001
|
-
});
|
|
4002
|
-
console.log('Sending healthCheckRequest to SFU', message);
|
|
4003
|
-
this.send(message);
|
|
4004
|
-
}, 27000);
|
|
4022
|
+
this.signalReady = new Promise((resolve) => {
|
|
4023
|
+
this.signalWs.addEventListener('open', () => {
|
|
4024
|
+
this.keepAlive();
|
|
4025
|
+
resolve(this.signalWs);
|
|
4026
|
+
});
|
|
4005
4027
|
});
|
|
4006
4028
|
}
|
|
4007
4029
|
}
|
|
@@ -4019,8 +4041,9 @@ function getIceCandidate(candidate) {
|
|
|
4019
4041
|
}
|
|
4020
4042
|
}
|
|
4021
4043
|
|
|
4022
|
-
const createSubscriber = ({ rpcClient, connectionConfig, onTrack, }) => {
|
|
4044
|
+
const createSubscriber = ({ rpcClient, dispatcher, connectionConfig, onTrack, }) => {
|
|
4023
4045
|
const subscriber = new RTCPeerConnection(connectionConfig);
|
|
4046
|
+
attachDebugEventListeners(subscriber);
|
|
4024
4047
|
subscriber.addEventListener('icecandidate', (e) => __awaiter(void 0, void 0, void 0, function* () {
|
|
4025
4048
|
const { candidate } = e;
|
|
4026
4049
|
if (!candidate) {
|
|
@@ -4032,22 +4055,11 @@ const createSubscriber = ({ rpcClient, connectionConfig, onTrack, }) => {
|
|
|
4032
4055
|
peerType: PeerType.SUBSCRIBER,
|
|
4033
4056
|
});
|
|
4034
4057
|
}));
|
|
4035
|
-
subscriber.addEventListener('icecandidateerror', (e) => {
|
|
4036
|
-
const errorMessage = e instanceof RTCPeerConnectionIceErrorEvent &&
|
|
4037
|
-
`${e.errorCode}: ${e.errorText}`;
|
|
4038
|
-
console.error(`Subscriber: ICE Candidate error`, errorMessage, e);
|
|
4039
|
-
});
|
|
4040
|
-
subscriber.addEventListener('iceconnectionstatechange', (e) => {
|
|
4041
|
-
console.log(`Subscriber: ICE Connection state changed`, subscriber.iceConnectionState, e);
|
|
4042
|
-
});
|
|
4043
|
-
subscriber.addEventListener('icegatheringstatechange', (e) => {
|
|
4044
|
-
console.log(`Subscriber: ICE Gathering State`, subscriber.iceGatheringState, e);
|
|
4045
|
-
});
|
|
4046
4058
|
if (onTrack) {
|
|
4047
4059
|
subscriber.addEventListener('track', onTrack);
|
|
4048
4060
|
}
|
|
4049
|
-
const {
|
|
4050
|
-
dispatcher.on('subscriberOffer', (message) => __awaiter(void 0, void 0, void 0, function* () {
|
|
4061
|
+
const { iceTrickleBuffer } = rpcClient;
|
|
4062
|
+
const unsubscribe = dispatcher.on('subscriberOffer', (message) => __awaiter(void 0, void 0, void 0, function* () {
|
|
4051
4063
|
if (message.eventPayload.oneofKind !== 'subscriberOffer')
|
|
4052
4064
|
return;
|
|
4053
4065
|
const { subscriberOffer } = message.eventPayload;
|
|
@@ -4062,7 +4074,7 @@ const createSubscriber = ({ rpcClient, connectionConfig, onTrack, }) => {
|
|
|
4062
4074
|
yield subscriber.addIceCandidate(iceCandidate);
|
|
4063
4075
|
}
|
|
4064
4076
|
catch (e) {
|
|
4065
|
-
console.error(`
|
|
4077
|
+
console.error(`Subscriber: ICE candidate error`, e, candidate);
|
|
4066
4078
|
}
|
|
4067
4079
|
}));
|
|
4068
4080
|
// apply ice candidates
|
|
@@ -4073,8 +4085,30 @@ const createSubscriber = ({ rpcClient, connectionConfig, onTrack, }) => {
|
|
|
4073
4085
|
sdp: answer.sdp || '',
|
|
4074
4086
|
});
|
|
4075
4087
|
}));
|
|
4088
|
+
// we replace the close method of the subscriber PeerConnection
|
|
4089
|
+
// so that we can preform some cleanups before closing the connection.
|
|
4090
|
+
// We are doing this as currently there is no event that is fired
|
|
4091
|
+
// when the subscriber PeerConnection is closed.
|
|
4092
|
+
const originalClose = subscriber.close;
|
|
4093
|
+
subscriber.close = () => {
|
|
4094
|
+
unsubscribe();
|
|
4095
|
+
originalClose.call(subscriber);
|
|
4096
|
+
};
|
|
4076
4097
|
return subscriber;
|
|
4077
4098
|
};
|
|
4099
|
+
const attachDebugEventListeners = (subscriber) => {
|
|
4100
|
+
subscriber.addEventListener('icecandidateerror', (e) => {
|
|
4101
|
+
const errorMessage = e instanceof RTCPeerConnectionIceErrorEvent &&
|
|
4102
|
+
`${e.errorCode}: ${e.errorText}`;
|
|
4103
|
+
console.error(`Subscriber: ICE Candidate error`, errorMessage);
|
|
4104
|
+
});
|
|
4105
|
+
subscriber.addEventListener('iceconnectionstatechange', () => {
|
|
4106
|
+
console.log(`Subscriber: ICE Connection state changed`, subscriber.iceConnectionState);
|
|
4107
|
+
});
|
|
4108
|
+
subscriber.addEventListener('icegatheringstatechange', () => {
|
|
4109
|
+
console.log(`Subscriber: ICE Gathering State`, subscriber.iceGatheringState);
|
|
4110
|
+
});
|
|
4111
|
+
};
|
|
4078
4112
|
|
|
4079
4113
|
const findOptimalVideoLayers = (videoTrack) => {
|
|
4080
4114
|
const steps = [
|
|
@@ -4221,8 +4255,8 @@ const getGenericSdp = (direction, preferredCodec) => __awaiter(void 0, void 0, v
|
|
|
4221
4255
|
});
|
|
4222
4256
|
|
|
4223
4257
|
/**
|
|
4224
|
-
* @internal
|
|
4225
4258
|
* The `Publisher` is responsible for publishing/unpublishing media streams to/from the SFU
|
|
4259
|
+
* @internal
|
|
4226
4260
|
*/
|
|
4227
4261
|
class Publisher {
|
|
4228
4262
|
constructor({ connectionConfig, rpcClient, isDtxEnabled }) {
|
|
@@ -4276,7 +4310,7 @@ class Publisher {
|
|
|
4276
4310
|
if (trackType === TrackType.VIDEO) {
|
|
4277
4311
|
const codecPreferences = getPreferredCodecs('video', opts.preferredCodec || 'vp8');
|
|
4278
4312
|
if ('setCodecPreferences' in transceiver && codecPreferences) {
|
|
4279
|
-
console.log(`set codec preferences`, codecPreferences);
|
|
4313
|
+
console.log(`set video codec preferences`, codecPreferences);
|
|
4280
4314
|
// @ts-ignore
|
|
4281
4315
|
transceiver.setCodecPreferences(codecPreferences);
|
|
4282
4316
|
}
|
|
@@ -4286,14 +4320,17 @@ class Publisher {
|
|
|
4286
4320
|
const codecPreferences = getPreferredCodecs('audio', opts.preferredCodec, returnOnlyMatched);
|
|
4287
4321
|
console.log('Preferred codec', opts.preferredCodec);
|
|
4288
4322
|
if ('setCodecPreferences' in transceiver && codecPreferences) {
|
|
4289
|
-
console.log(`set codec preferences`, codecPreferences);
|
|
4323
|
+
console.log(`set audio codec preferences`, codecPreferences);
|
|
4290
4324
|
// @ts-ignore
|
|
4291
4325
|
transceiver.setCodecPreferences(codecPreferences);
|
|
4292
4326
|
}
|
|
4293
4327
|
}
|
|
4294
4328
|
}
|
|
4295
4329
|
else {
|
|
4296
|
-
|
|
4330
|
+
// don't stop the track if we are re-publishing the same track
|
|
4331
|
+
if (transceiver.sender.track !== track) {
|
|
4332
|
+
(_a = transceiver.sender.track) === null || _a === void 0 ? void 0 : _a.stop();
|
|
4333
|
+
}
|
|
4297
4334
|
yield transceiver.sender.replaceTrack(track);
|
|
4298
4335
|
}
|
|
4299
4336
|
});
|
|
@@ -4317,16 +4354,21 @@ class Publisher {
|
|
|
4317
4354
|
};
|
|
4318
4355
|
/**
|
|
4319
4356
|
* Stops publishing all tracks and stop all tracks.
|
|
4357
|
+
*
|
|
4358
|
+
* @param options - Options
|
|
4359
|
+
* @param options.stopTracks - If `true` (default), all tracks will be stopped.
|
|
4320
4360
|
*/
|
|
4321
|
-
this.stopPublishing = () => {
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
this.publisher.
|
|
4328
|
-
|
|
4329
|
-
|
|
4361
|
+
this.stopPublishing = (options = {}) => {
|
|
4362
|
+
const { stopTracks = true } = options;
|
|
4363
|
+
if (stopTracks) {
|
|
4364
|
+
this.publisher.getSenders().forEach((s) => {
|
|
4365
|
+
var _a;
|
|
4366
|
+
(_a = s.track) === null || _a === void 0 ? void 0 : _a.stop();
|
|
4367
|
+
if (this.publisher.signalingState !== 'closed') {
|
|
4368
|
+
this.publisher.removeTrack(s);
|
|
4369
|
+
}
|
|
4370
|
+
});
|
|
4371
|
+
}
|
|
4330
4372
|
this.publisher.close();
|
|
4331
4373
|
};
|
|
4332
4374
|
this.updateVideoPublishQuality = (enabledRids) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -4400,34 +4442,39 @@ class Publisher {
|
|
|
4400
4442
|
};
|
|
4401
4443
|
});
|
|
4402
4444
|
// TODO debounce for 250ms
|
|
4403
|
-
const response = yield this.rpcClient.setPublisher({
|
|
4445
|
+
const { response } = yield this.rpcClient.setPublisher({
|
|
4404
4446
|
sdp: offer.sdp || '',
|
|
4405
4447
|
tracks: trackInfos,
|
|
4406
4448
|
});
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4449
|
+
try {
|
|
4450
|
+
yield this.publisher.setRemoteDescription({
|
|
4451
|
+
type: 'answer',
|
|
4452
|
+
sdp: response.sdp,
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4455
|
+
catch (e) {
|
|
4456
|
+
console.error(`Publisher: setRemoteDescription error`, response.sdp, e);
|
|
4457
|
+
}
|
|
4411
4458
|
this.rpcClient.iceTrickleBuffer.publisherCandidates.subscribe((candidate) => __awaiter(this, void 0, void 0, function* () {
|
|
4412
4459
|
try {
|
|
4413
4460
|
const iceCandidate = JSON.parse(candidate.iceCandidate);
|
|
4414
4461
|
yield this.publisher.addIceCandidate(iceCandidate);
|
|
4415
4462
|
}
|
|
4416
4463
|
catch (e) {
|
|
4417
|
-
console.error(`
|
|
4464
|
+
console.error(`Publisher: ICE candidate error`, e, candidate);
|
|
4418
4465
|
}
|
|
4419
4466
|
}));
|
|
4420
4467
|
});
|
|
4421
4468
|
this.onIceCandidateError = (e) => {
|
|
4422
4469
|
const errorMessage = e instanceof RTCPeerConnectionIceErrorEvent &&
|
|
4423
4470
|
`${e.errorCode}: ${e.errorText}`;
|
|
4424
|
-
console.error(`Publisher: ICE Candidate error`, errorMessage
|
|
4471
|
+
console.error(`Publisher: ICE Candidate error`, errorMessage);
|
|
4425
4472
|
};
|
|
4426
|
-
this.onIceConnectionStateChange = (
|
|
4427
|
-
console.log(`Publisher: ICE Connection state changed`, this.publisher.iceConnectionState
|
|
4473
|
+
this.onIceConnectionStateChange = () => {
|
|
4474
|
+
console.log(`Publisher: ICE Connection state changed`, this.publisher.iceConnectionState);
|
|
4428
4475
|
};
|
|
4429
|
-
this.onIceGatheringStateChange = (
|
|
4430
|
-
console.log(`Publisher: ICE Gathering State`, this.publisher.iceGatheringState
|
|
4476
|
+
this.onIceGatheringStateChange = () => {
|
|
4477
|
+
console.log(`Publisher: ICE Gathering State`, this.publisher.iceGatheringState);
|
|
4431
4478
|
};
|
|
4432
4479
|
this.ridToVideoQuality = (rid) => {
|
|
4433
4480
|
return rid === 'q'
|
|
@@ -4606,6 +4653,25 @@ const registerEventHandlers = (call, store, dispatcher) => {
|
|
|
4606
4653
|
watchDominantSpeakerChanged(dispatcher, store);
|
|
4607
4654
|
};
|
|
4608
4655
|
|
|
4656
|
+
const sfuEventKinds = {
|
|
4657
|
+
subscriberOffer: undefined,
|
|
4658
|
+
publisherAnswer: undefined,
|
|
4659
|
+
connectionQualityChanged: undefined,
|
|
4660
|
+
audioLevelChanged: undefined,
|
|
4661
|
+
iceTrickle: undefined,
|
|
4662
|
+
changePublishQuality: undefined,
|
|
4663
|
+
participantJoined: undefined,
|
|
4664
|
+
participantLeft: undefined,
|
|
4665
|
+
dominantSpeakerChanged: undefined,
|
|
4666
|
+
joinResponse: undefined,
|
|
4667
|
+
healthCheckResponse: undefined,
|
|
4668
|
+
trackPublished: undefined,
|
|
4669
|
+
trackUnpublished: undefined,
|
|
4670
|
+
error: undefined,
|
|
4671
|
+
};
|
|
4672
|
+
const isSfuEvent = (eventName) => {
|
|
4673
|
+
return Object.prototype.hasOwnProperty.call(sfuEventKinds, eventName);
|
|
4674
|
+
};
|
|
4609
4675
|
class Dispatcher {
|
|
4610
4676
|
constructor() {
|
|
4611
4677
|
this.subscribers = {};
|
|
@@ -4959,6 +5025,40 @@ const speakerLayoutSortPreset = combineComparators(pinned, screenSharing, domina
|
|
|
4959
5025
|
*/
|
|
4960
5026
|
const livestreamOrAudioRoomSortPreset = combineComparators(ifInvisibleBy(dominantSpeaker), ifInvisibleBy(speaking), ifInvisibleBy(reactionType('raised-hand')), ifInvisibleBy(publishingVideo), ifInvisibleBy(publishingAudio), role('admin', 'host', 'speaker'));
|
|
4961
5027
|
|
|
5028
|
+
/**
|
|
5029
|
+
* Represents the state of the current call.
|
|
5030
|
+
*/
|
|
5031
|
+
var CallingState;
|
|
5032
|
+
(function (CallingState) {
|
|
5033
|
+
/**
|
|
5034
|
+
* The call is in an idle state.
|
|
5035
|
+
*/
|
|
5036
|
+
CallingState["IDLE"] = "idle";
|
|
5037
|
+
/**
|
|
5038
|
+
* The call is in the process of joining.
|
|
5039
|
+
*/
|
|
5040
|
+
CallingState["JOINING"] = "joining";
|
|
5041
|
+
/**
|
|
5042
|
+
* The call is currently active.
|
|
5043
|
+
*/
|
|
5044
|
+
CallingState["JOINED"] = "joined";
|
|
5045
|
+
/**
|
|
5046
|
+
* The call has been left.
|
|
5047
|
+
*/
|
|
5048
|
+
CallingState["LEFT"] = "left";
|
|
5049
|
+
/**
|
|
5050
|
+
* The call is in the process of reconnecting.
|
|
5051
|
+
*/
|
|
5052
|
+
CallingState["RECONNECTING"] = "reconnecting";
|
|
5053
|
+
/**
|
|
5054
|
+
* The call has failed to reconnect.
|
|
5055
|
+
*/
|
|
5056
|
+
CallingState["RECONNECTING_FAILED"] = "reconnecting-failed";
|
|
5057
|
+
/**
|
|
5058
|
+
* The call is in offline mode.
|
|
5059
|
+
*/
|
|
5060
|
+
CallingState["OFFLINE"] = "offline";
|
|
5061
|
+
})(CallingState || (CallingState = {}));
|
|
4962
5062
|
/**
|
|
4963
5063
|
* Holds the state of the current call.
|
|
4964
5064
|
* @react You don't have to use this class directly, as we are exposing the state through Hooks.
|
|
@@ -4982,6 +5082,12 @@ class CallState {
|
|
|
4982
5082
|
* @internal
|
|
4983
5083
|
*/
|
|
4984
5084
|
this.membersSubject = new BehaviorSubject([]);
|
|
5085
|
+
/**
|
|
5086
|
+
* The calling state.
|
|
5087
|
+
*
|
|
5088
|
+
* @internal
|
|
5089
|
+
*/
|
|
5090
|
+
this.callingStateSubject = new BehaviorSubject(CallingState.IDLE);
|
|
4985
5091
|
/**
|
|
4986
5092
|
* All participants of the current call (including the logged-in user).
|
|
4987
5093
|
*
|
|
@@ -5123,6 +5229,7 @@ class CallState {
|
|
|
5123
5229
|
return acc;
|
|
5124
5230
|
}, {});
|
|
5125
5231
|
}));
|
|
5232
|
+
this.callingState$ = this.callingStateSubject.asObservable();
|
|
5126
5233
|
}
|
|
5127
5234
|
}
|
|
5128
5235
|
|
|
@@ -5620,6 +5727,129 @@ const CallTypes = new CallTypesRegistry([
|
|
|
5620
5727
|
}),
|
|
5621
5728
|
]);
|
|
5622
5729
|
|
|
5730
|
+
const sleep = (m) => new Promise((r) => setTimeout(r, m));
|
|
5731
|
+
function isFunction(value) {
|
|
5732
|
+
return (value &&
|
|
5733
|
+
(Object.prototype.toString.call(value) === '[object Function]' ||
|
|
5734
|
+
'function' === typeof value ||
|
|
5735
|
+
value instanceof Function));
|
|
5736
|
+
}
|
|
5737
|
+
// todo: rename so that it does not contain word "chat"
|
|
5738
|
+
const chatCodes = {
|
|
5739
|
+
TOKEN_EXPIRED: 40,
|
|
5740
|
+
WS_CLOSED_SUCCESS: 1000,
|
|
5741
|
+
};
|
|
5742
|
+
/**
|
|
5743
|
+
* retryInterval - A retry interval which increases acc to number of failures
|
|
5744
|
+
*
|
|
5745
|
+
* @return {number} Duration to wait in milliseconds
|
|
5746
|
+
*/
|
|
5747
|
+
function retryInterval(numberOfFailures) {
|
|
5748
|
+
// try to reconnect in 0.25-5 seconds (random to spread out the load from failures)
|
|
5749
|
+
const max = Math.min(500 + numberOfFailures * 2000, 5000);
|
|
5750
|
+
const min = Math.min(Math.max(250, (numberOfFailures - 1) * 2000), 5000);
|
|
5751
|
+
return Math.floor(Math.random() * (max - min) + min);
|
|
5752
|
+
}
|
|
5753
|
+
function randomId() {
|
|
5754
|
+
return generateUUIDv4();
|
|
5755
|
+
}
|
|
5756
|
+
function hex(bytes) {
|
|
5757
|
+
let s = '';
|
|
5758
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
5759
|
+
s += bytes[i].toString(16).padStart(2, '0');
|
|
5760
|
+
}
|
|
5761
|
+
return s;
|
|
5762
|
+
}
|
|
5763
|
+
// https://tools.ietf.org/html/rfc4122
|
|
5764
|
+
function generateUUIDv4() {
|
|
5765
|
+
const bytes = getRandomBytes(16);
|
|
5766
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version
|
|
5767
|
+
bytes[8] = (bytes[8] & 0xbf) | 0x80; // variant
|
|
5768
|
+
return (hex(bytes.subarray(0, 4)) +
|
|
5769
|
+
'-' +
|
|
5770
|
+
hex(bytes.subarray(4, 6)) +
|
|
5771
|
+
'-' +
|
|
5772
|
+
hex(bytes.subarray(6, 8)) +
|
|
5773
|
+
'-' +
|
|
5774
|
+
hex(bytes.subarray(8, 10)) +
|
|
5775
|
+
'-' +
|
|
5776
|
+
hex(bytes.subarray(10, 16)));
|
|
5777
|
+
}
|
|
5778
|
+
function getRandomValuesWithMathRandom(bytes) {
|
|
5779
|
+
const max = Math.pow(2, (8 * bytes.byteLength) / bytes.length);
|
|
5780
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
5781
|
+
bytes[i] = Math.random() * max;
|
|
5782
|
+
}
|
|
5783
|
+
}
|
|
5784
|
+
const getRandomValues = (() => {
|
|
5785
|
+
if (typeof crypto !== 'undefined' &&
|
|
5786
|
+
typeof (crypto === null || crypto === void 0 ? void 0 : crypto.getRandomValues) !== 'undefined') {
|
|
5787
|
+
return crypto.getRandomValues.bind(crypto);
|
|
5788
|
+
}
|
|
5789
|
+
else if (typeof msCrypto !== 'undefined') {
|
|
5790
|
+
return msCrypto.getRandomValues.bind(msCrypto);
|
|
5791
|
+
}
|
|
5792
|
+
else {
|
|
5793
|
+
return getRandomValuesWithMathRandom;
|
|
5794
|
+
}
|
|
5795
|
+
})();
|
|
5796
|
+
function getRandomBytes(length) {
|
|
5797
|
+
const bytes = new Uint8Array(length);
|
|
5798
|
+
getRandomValues(bytes);
|
|
5799
|
+
return bytes;
|
|
5800
|
+
}
|
|
5801
|
+
function convertErrorToJson(err) {
|
|
5802
|
+
const jsonObj = {};
|
|
5803
|
+
if (!err)
|
|
5804
|
+
return jsonObj;
|
|
5805
|
+
try {
|
|
5806
|
+
Object.getOwnPropertyNames(err).forEach((key) => {
|
|
5807
|
+
jsonObj[key] = Object.getOwnPropertyDescriptor(err, key);
|
|
5808
|
+
});
|
|
5809
|
+
}
|
|
5810
|
+
catch (_) {
|
|
5811
|
+
return {
|
|
5812
|
+
error: 'failed to serialize the error',
|
|
5813
|
+
};
|
|
5814
|
+
}
|
|
5815
|
+
return jsonObj;
|
|
5816
|
+
}
|
|
5817
|
+
/**
|
|
5818
|
+
* isOnline safely return the navigator.online value for browser env
|
|
5819
|
+
* if navigator is not in global object, it always return true
|
|
5820
|
+
*/
|
|
5821
|
+
function isOnline() {
|
|
5822
|
+
const nav = typeof navigator !== 'undefined'
|
|
5823
|
+
? navigator
|
|
5824
|
+
: typeof window !== 'undefined' && window.navigator
|
|
5825
|
+
? window.navigator
|
|
5826
|
+
: undefined;
|
|
5827
|
+
if (!nav) {
|
|
5828
|
+
console.warn('isOnline failed to access window.navigator and assume browser is online');
|
|
5829
|
+
return true;
|
|
5830
|
+
}
|
|
5831
|
+
// RN navigator has undefined for onLine
|
|
5832
|
+
if (typeof nav.onLine !== 'boolean') {
|
|
5833
|
+
return true;
|
|
5834
|
+
}
|
|
5835
|
+
return nav.onLine;
|
|
5836
|
+
}
|
|
5837
|
+
/**
|
|
5838
|
+
* listenForConnectionChanges - Adds an event listener fired on browser going online or offline
|
|
5839
|
+
*/
|
|
5840
|
+
function addConnectionEventListeners(cb) {
|
|
5841
|
+
if (typeof window !== 'undefined' && window.addEventListener) {
|
|
5842
|
+
window.addEventListener('offline', cb);
|
|
5843
|
+
window.addEventListener('online', cb);
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
5846
|
+
function removeConnectionEventListeners(cb) {
|
|
5847
|
+
if (typeof window !== 'undefined' && window.removeEventListener) {
|
|
5848
|
+
window.removeEventListener('offline', cb);
|
|
5849
|
+
window.removeEventListener('online', cb);
|
|
5850
|
+
}
|
|
5851
|
+
}
|
|
5852
|
+
|
|
5623
5853
|
const UPDATE_SUBSCRIPTIONS_DEBOUNCE_DURATION = 600;
|
|
5624
5854
|
/**
|
|
5625
5855
|
* A `Call` object represents the active call the user is part of.
|
|
@@ -5628,7 +5858,7 @@ const UPDATE_SUBSCRIPTIONS_DEBOUNCE_DURATION = 600;
|
|
|
5628
5858
|
class Call {
|
|
5629
5859
|
get preferredAudioCodec() {
|
|
5630
5860
|
var _a;
|
|
5631
|
-
const audioSettings = (_a = this.
|
|
5861
|
+
const audioSettings = (_a = this.data) === null || _a === void 0 ? void 0 : _a.settings.audio;
|
|
5632
5862
|
let preferredCodec = (audioSettings === null || audioSettings === void 0 ? void 0 : audioSettings.redundant_coding_enabled) === undefined
|
|
5633
5863
|
? 'opus'
|
|
5634
5864
|
: audioSettings.redundant_coding_enabled
|
|
@@ -5647,7 +5877,7 @@ class Call {
|
|
|
5647
5877
|
*/
|
|
5648
5878
|
constructor({ type, id, streamClient, metadata, members, sortParticipantsBy, clientStore, }) {
|
|
5649
5879
|
/**
|
|
5650
|
-
*
|
|
5880
|
+
* ViewportTracker instance
|
|
5651
5881
|
*/
|
|
5652
5882
|
this.viewportTracker = new ViewportTracker();
|
|
5653
5883
|
/**
|
|
@@ -5656,17 +5886,7 @@ class Call {
|
|
|
5656
5886
|
*/
|
|
5657
5887
|
this.dispatcher = new Dispatcher();
|
|
5658
5888
|
this.trackSubscriptionsSubject = new Subject();
|
|
5659
|
-
this.
|
|
5660
|
-
/**
|
|
5661
|
-
* You can subscribe to WebSocket events provided by the API. To remove a subscription, call the `off` method.
|
|
5662
|
-
* Please note that subscribing to WebSocket events is an advanced use-case, for most use-cases it should be enough to watch for changes in the [reactive state store](./StreamVideoClient.md/#readonlystatestore).
|
|
5663
|
-
* @param eventName
|
|
5664
|
-
* @param fn
|
|
5665
|
-
* @returns
|
|
5666
|
-
*/
|
|
5667
|
-
this.on = (eventName, fn) => {
|
|
5668
|
-
return this.dispatcher.on(eventName, fn);
|
|
5669
|
-
};
|
|
5889
|
+
this.reconnectAttempts = 0;
|
|
5670
5890
|
/**
|
|
5671
5891
|
* Remove subscription for WebSocket events that were created by the `on` method.
|
|
5672
5892
|
* @param eventName
|
|
@@ -5681,10 +5901,9 @@ class Call {
|
|
|
5681
5901
|
*/
|
|
5682
5902
|
this.leave = () => {
|
|
5683
5903
|
var _a, _b, _c, _d;
|
|
5684
|
-
if (
|
|
5904
|
+
if (this.state.getCurrentValue(this.state.callingState$) === CallingState.LEFT) {
|
|
5685
5905
|
throw new Error('Cannot leave call that has already been left.');
|
|
5686
5906
|
}
|
|
5687
|
-
this.joined$.next(false);
|
|
5688
5907
|
(_a = this.statsReporter) === null || _a === void 0 ? void 0 : _a.stop();
|
|
5689
5908
|
this.statsReporter = undefined;
|
|
5690
5909
|
(_b = this.subscriber) === null || _b === void 0 ? void 0 : _b.close();
|
|
@@ -5693,13 +5912,19 @@ class Call {
|
|
|
5693
5912
|
this.publisher = undefined;
|
|
5694
5913
|
(_d = this.sfuClient) === null || _d === void 0 ? void 0 : _d.close();
|
|
5695
5914
|
this.sfuClient = undefined;
|
|
5915
|
+
this.dispatcher.offAll();
|
|
5696
5916
|
this.clientStore.setCurrentValue(this.clientStore.activeCallSubject, undefined);
|
|
5917
|
+
this.state.setCurrentValue(this.state.callingStateSubject, CallingState.LEFT);
|
|
5697
5918
|
};
|
|
5698
|
-
this.waitForJoinResponse = (timeout =
|
|
5919
|
+
this.waitForJoinResponse = (timeout = 5000) => new Promise((resolve, reject) => {
|
|
5699
5920
|
const unsubscribe = this.on('joinResponse', (event) => {
|
|
5700
|
-
|
|
5921
|
+
if (event.eventPayload.oneofKind !== 'joinResponse')
|
|
5922
|
+
return;
|
|
5923
|
+
clearTimeout(timeoutId);
|
|
5924
|
+
unsubscribe();
|
|
5925
|
+
resolve(event.eventPayload.joinResponse);
|
|
5701
5926
|
});
|
|
5702
|
-
setTimeout(() => {
|
|
5927
|
+
const timeoutId = setTimeout(() => {
|
|
5703
5928
|
unsubscribe();
|
|
5704
5929
|
reject(new Error('Waiting for "joinResponse" has timed out'));
|
|
5705
5930
|
}, timeout);
|
|
@@ -5722,9 +5947,10 @@ class Call {
|
|
|
5722
5947
|
*/
|
|
5723
5948
|
this.join = (data) => __awaiter(this, void 0, void 0, function* () {
|
|
5724
5949
|
var _a;
|
|
5725
|
-
if (this.
|
|
5950
|
+
if ([CallingState.JOINED, CallingState.JOINING].includes(this.state.getCurrentValue(this.state.callingState$))) {
|
|
5726
5951
|
throw new Error(`Illegal State: Already joined.`);
|
|
5727
5952
|
}
|
|
5953
|
+
this.state.setCurrentValue(this.state.callingStateSubject, CallingState.JOINING);
|
|
5728
5954
|
const call = yield join(this.streamClient, this.type, this.id, data);
|
|
5729
5955
|
this.state.setCurrentValue(this.state.metadataSubject, call.metadata);
|
|
5730
5956
|
this.state.setCurrentValue(this.state.membersSubject, call.members);
|
|
@@ -5740,12 +5966,79 @@ class Call {
|
|
|
5740
5966
|
sfuUrl = sfuUrlParam || call.sfuServer.url;
|
|
5741
5967
|
}
|
|
5742
5968
|
const sfuClient = (this.sfuClient = new StreamSfuClient(this.dispatcher, sfuUrl, call.token));
|
|
5969
|
+
/**
|
|
5970
|
+
* A closure which hides away the re-connection logic.
|
|
5971
|
+
*/
|
|
5972
|
+
const rejoin = () => __awaiter(this, void 0, void 0, function* () {
|
|
5973
|
+
var _b, _c, _d;
|
|
5974
|
+
console.log(`Rejoining call ${this.cid} (${this.reconnectAttempts})...`);
|
|
5975
|
+
this.reconnectAttempts++;
|
|
5976
|
+
this.state.setCurrentValue(this.state.callingStateSubject, CallingState.RECONNECTING);
|
|
5977
|
+
// take a snapshot of the current "local participant" state
|
|
5978
|
+
// we'll need it for restoring the previous publishing state later
|
|
5979
|
+
const localParticipant = this.state.getCurrentValue(this.state.localParticipant$);
|
|
5980
|
+
(_b = this.subscriber) === null || _b === void 0 ? void 0 : _b.close();
|
|
5981
|
+
(_c = this.publisher) === null || _c === void 0 ? void 0 : _c.stopPublishing({ stopTracks: false });
|
|
5982
|
+
(_d = this.statsReporter) === null || _d === void 0 ? void 0 : _d.stop();
|
|
5983
|
+
sfuClient.close(); // clean up previous connection
|
|
5984
|
+
yield sleep(retryInterval(this.reconnectAttempts));
|
|
5985
|
+
yield this.join(data);
|
|
5986
|
+
console.log(`Rejoin: ${this.reconnectAttempts} successful!`);
|
|
5987
|
+
if (localParticipant) {
|
|
5988
|
+
const { audioStream, videoStream, screenShareStream: screenShare, } = localParticipant;
|
|
5989
|
+
// restore previous publishing state
|
|
5990
|
+
if (audioStream)
|
|
5991
|
+
yield this.publishAudioStream(audioStream);
|
|
5992
|
+
if (videoStream)
|
|
5993
|
+
yield this.publishVideoStream(videoStream);
|
|
5994
|
+
if (screenShare)
|
|
5995
|
+
yield this.publishScreenShareStream(screenShare);
|
|
5996
|
+
}
|
|
5997
|
+
console.log(`Rejoin: state restored ${this.reconnectAttempts}`);
|
|
5998
|
+
});
|
|
5999
|
+
// reconnect if the connection was closed unexpectedly. example:
|
|
6000
|
+
// - SFU crash or restart
|
|
6001
|
+
// - network change
|
|
6002
|
+
sfuClient.signalReady.then(() => {
|
|
6003
|
+
sfuClient.signalWs.addEventListener('close', (e) => {
|
|
6004
|
+
// do nothing if the connection was closed on purpose
|
|
6005
|
+
if (e.code === 1000)
|
|
6006
|
+
return;
|
|
6007
|
+
if (this.reconnectAttempts >= 10) {
|
|
6008
|
+
console.log('Reconnect attempts exceeded. Giving up...');
|
|
6009
|
+
this.state.setCurrentValue(this.state.callingStateSubject, CallingState.RECONNECTING_FAILED);
|
|
6010
|
+
}
|
|
6011
|
+
else {
|
|
6012
|
+
void rejoin();
|
|
6013
|
+
}
|
|
6014
|
+
});
|
|
6015
|
+
});
|
|
6016
|
+
// handlers for connection online/offline events
|
|
6017
|
+
// Note: window.addEventListener is not available in React Native, hence the check
|
|
6018
|
+
if (typeof window !== 'undefined' && window.addEventListener) {
|
|
6019
|
+
const handleOnOffline = () => {
|
|
6020
|
+
window.removeEventListener('offline', handleOnOffline);
|
|
6021
|
+
console.log('Join: Going offline...');
|
|
6022
|
+
this.state.setCurrentValue(this.state.callingStateSubject, CallingState.OFFLINE);
|
|
6023
|
+
};
|
|
6024
|
+
const handleOnOnline = () => {
|
|
6025
|
+
window.removeEventListener('online', handleOnOnline);
|
|
6026
|
+
if (this.state.getCurrentValue(this.state.callingState$) ===
|
|
6027
|
+
CallingState.OFFLINE) {
|
|
6028
|
+
console.log('Join: Going online...');
|
|
6029
|
+
rejoin();
|
|
6030
|
+
}
|
|
6031
|
+
};
|
|
6032
|
+
window.addEventListener('offline', handleOnOffline);
|
|
6033
|
+
window.addEventListener('online', handleOnOnline);
|
|
6034
|
+
}
|
|
5743
6035
|
this.subscriber = createSubscriber({
|
|
5744
6036
|
rpcClient: sfuClient,
|
|
6037
|
+
dispatcher: this.dispatcher,
|
|
5745
6038
|
connectionConfig: call.connectionConfig,
|
|
5746
6039
|
onTrack: this.handleOnTrack,
|
|
5747
6040
|
});
|
|
5748
|
-
const audioSettings = (_a = this.
|
|
6041
|
+
const audioSettings = (_a = this.data) === null || _a === void 0 ? void 0 : _a.settings.audio;
|
|
5749
6042
|
let isDtxEnabled = (audioSettings === null || audioSettings === void 0 ? void 0 : audioSettings.opus_dtx_enabled) === undefined
|
|
5750
6043
|
? false
|
|
5751
6044
|
: audioSettings === null || audioSettings === void 0 ? void 0 : audioSettings.opus_dtx_enabled;
|
|
@@ -5768,8 +6061,19 @@ class Call {
|
|
|
5768
6061
|
store: this.state,
|
|
5769
6062
|
edgeName: call.sfuServer.edge_name,
|
|
5770
6063
|
});
|
|
5771
|
-
|
|
5772
|
-
|
|
6064
|
+
try {
|
|
6065
|
+
// 1. wait for the signal server to be ready before sending "joinRequest"
|
|
6066
|
+
sfuClient.signalReady
|
|
6067
|
+
.catch((err) => console.warn('Signal ready failed', err))
|
|
6068
|
+
// prepare a generic SDP and send it to the SFU.
|
|
6069
|
+
// this is a throw-away SDP that the SFU will use to determine
|
|
6070
|
+
// the capabilities of the client (codec support, etc.)
|
|
6071
|
+
.then(() => getGenericSdp('recvonly', this.preferredAudioCodec))
|
|
6072
|
+
.then((sdp) => sfuClient.join({ subscriberSdp: sdp || '' }));
|
|
6073
|
+
// 2. in parallel, wait for the SFU to send us the "joinResponse"
|
|
6074
|
+
// this will throw an error if the SFU rejects the join request or
|
|
6075
|
+
// fails to respond in time
|
|
6076
|
+
const { callState } = yield this.waitForJoinResponse();
|
|
5773
6077
|
const currentParticipants = (callState === null || callState === void 0 ? void 0 : callState.participants) || [];
|
|
5774
6078
|
const ownCapabilities = {
|
|
5775
6079
|
ownCapabilities: call.metadata.own_capabilities,
|
|
@@ -5778,14 +6082,22 @@ class Call {
|
|
|
5778
6082
|
? ownCapabilities
|
|
5779
6083
|
: {})))));
|
|
5780
6084
|
this.clientStore.setCurrentValue(this.clientStore.activeCallSubject, this);
|
|
5781
|
-
|
|
5782
|
-
this.
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
6085
|
+
this.reconnectAttempts = 0; // reset the reconnect attempts counter
|
|
6086
|
+
this.state.setCurrentValue(this.state.callingStateSubject, CallingState.JOINED);
|
|
6087
|
+
console.log(`Joined call ${this.cid}`);
|
|
6088
|
+
}
|
|
6089
|
+
catch (err) {
|
|
6090
|
+
// join failed, try to rejoin
|
|
6091
|
+
if (this.reconnectAttempts < 10) {
|
|
6092
|
+
yield rejoin();
|
|
6093
|
+
console.log(`Rejoin ${this.reconnectAttempts} successful!`);
|
|
6094
|
+
}
|
|
6095
|
+
else {
|
|
6096
|
+
console.log(`Rejoin failed for ${this.reconnectAttempts} times. Giving up.`);
|
|
6097
|
+
this.state.setCurrentValue(this.state.callingStateSubject, CallingState.RECONNECTING_FAILED);
|
|
6098
|
+
throw new Error('Join failed');
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
5789
6101
|
});
|
|
5790
6102
|
this.updateCallMembers = (data) => __awaiter(this, void 0, void 0, function* () {
|
|
5791
6103
|
return yield this.streamClient.post(`${this.streamClientBasePath}/members`, data);
|
|
@@ -5805,7 +6117,7 @@ class Call {
|
|
|
5805
6117
|
* @param opts the options to use when publishing the stream.
|
|
5806
6118
|
*/
|
|
5807
6119
|
this.publishVideoStream = (videoStream, opts = {}) => __awaiter(this, void 0, void 0, function* () {
|
|
5808
|
-
var
|
|
6120
|
+
var _e;
|
|
5809
6121
|
// we should wait until we get a JoinResponse from the SFU,
|
|
5810
6122
|
// otherwise we risk breaking the ICETrickle flow.
|
|
5811
6123
|
yield this.assertCallJoined();
|
|
@@ -5819,7 +6131,7 @@ class Call {
|
|
|
5819
6131
|
const trackType = TrackType.VIDEO;
|
|
5820
6132
|
try {
|
|
5821
6133
|
yield this.publisher.publishStream(videoStream, videoTrack, trackType, opts);
|
|
5822
|
-
yield ((
|
|
6134
|
+
yield ((_e = this.sfuClient) === null || _e === void 0 ? void 0 : _e.updateMuteState(trackType, false));
|
|
5823
6135
|
}
|
|
5824
6136
|
catch (e) {
|
|
5825
6137
|
throw e;
|
|
@@ -6004,8 +6316,8 @@ class Call {
|
|
|
6004
6316
|
* @returns
|
|
6005
6317
|
*/
|
|
6006
6318
|
this.getStats = (kind, selector) => __awaiter(this, void 0, void 0, function* () {
|
|
6007
|
-
var
|
|
6008
|
-
return (
|
|
6319
|
+
var _f;
|
|
6320
|
+
return (_f = this.statsReporter) === null || _f === void 0 ? void 0 : _f.getRawStatsForTrack(kind, selector);
|
|
6009
6321
|
});
|
|
6010
6322
|
/**
|
|
6011
6323
|
* Will enhance the reported stats with additional participant-specific information (`callStatsReport$` state [store variable](./StreamVideoClient.md/#readonlystatestore)).
|
|
@@ -6093,8 +6405,8 @@ class Call {
|
|
|
6093
6405
|
* @returns
|
|
6094
6406
|
*/
|
|
6095
6407
|
this.updatePublishQuality = (enabledRids) => __awaiter(this, void 0, void 0, function* () {
|
|
6096
|
-
var
|
|
6097
|
-
return (
|
|
6408
|
+
var _g;
|
|
6409
|
+
return (_g = this.publisher) === null || _g === void 0 ? void 0 : _g.updateVideoPublishQuality(enabledRids);
|
|
6098
6410
|
});
|
|
6099
6411
|
this.handleOnTrack = (e) => {
|
|
6100
6412
|
const [primaryStream] = e.streams;
|
|
@@ -6140,11 +6452,9 @@ class Call {
|
|
|
6140
6452
|
};
|
|
6141
6453
|
this.assertCallJoined = () => {
|
|
6142
6454
|
return new Promise((resolve) => {
|
|
6143
|
-
this.
|
|
6144
|
-
.pipe(takeWhile((
|
|
6145
|
-
.subscribe(() =>
|
|
6146
|
-
resolve();
|
|
6147
|
-
});
|
|
6455
|
+
this.state.callingState$
|
|
6456
|
+
.pipe(takeWhile((state) => state !== CallingState.JOINED, true))
|
|
6457
|
+
.subscribe(() => resolve());
|
|
6148
6458
|
});
|
|
6149
6459
|
};
|
|
6150
6460
|
this.sendReaction = (reaction) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -6349,6 +6659,19 @@ class Call {
|
|
|
6349
6659
|
(_a = this.sfuClient) === null || _a === void 0 ? void 0 : _a.updateSubscriptions(subscriptions);
|
|
6350
6660
|
});
|
|
6351
6661
|
}
|
|
6662
|
+
on(eventName, fn) {
|
|
6663
|
+
if (isSfuEvent(eventName)) {
|
|
6664
|
+
return this.dispatcher.on(eventName, fn);
|
|
6665
|
+
}
|
|
6666
|
+
else {
|
|
6667
|
+
const eventHandler = (event) => {
|
|
6668
|
+
if (event.call_cid && event.call_cid === this.cid) {
|
|
6669
|
+
fn(event);
|
|
6670
|
+
}
|
|
6671
|
+
};
|
|
6672
|
+
return this.streamClient.on(eventName, eventHandler);
|
|
6673
|
+
}
|
|
6674
|
+
}
|
|
6352
6675
|
get data() {
|
|
6353
6676
|
return this.state.getCurrentValue(this.state.metadata$);
|
|
6354
6677
|
}
|
|
@@ -6361,6 +6684,9 @@ class Call {
|
|
|
6361
6684
|
*/
|
|
6362
6685
|
const watchCallCreated = (store, streamClient) => {
|
|
6363
6686
|
return function onCallCreated(event) {
|
|
6687
|
+
if (event.type !== 'call.created') {
|
|
6688
|
+
return;
|
|
6689
|
+
}
|
|
6364
6690
|
const { call, members } = event;
|
|
6365
6691
|
if (!call) {
|
|
6366
6692
|
console.warn("Can't find call in CallCreatedEvent");
|
|
@@ -6391,6 +6717,9 @@ const watchCallCreated = (store, streamClient) => {
|
|
|
6391
6717
|
*/
|
|
6392
6718
|
const watchCallAccepted = (store) => {
|
|
6393
6719
|
return function onCallAccepted(event) {
|
|
6720
|
+
if (event.type !== 'call.accepted') {
|
|
6721
|
+
return;
|
|
6722
|
+
}
|
|
6394
6723
|
const { call_cid } = event;
|
|
6395
6724
|
if (!call_cid) {
|
|
6396
6725
|
console.warn("Can't find call_cid in CallAcceptedEvent");
|
|
@@ -6429,6 +6758,9 @@ const watchCallAccepted = (store) => {
|
|
|
6429
6758
|
*/
|
|
6430
6759
|
const watchCallRejected = (store) => {
|
|
6431
6760
|
return function onCallRejected(event) {
|
|
6761
|
+
if (event.type !== 'call.rejected') {
|
|
6762
|
+
return;
|
|
6763
|
+
}
|
|
6432
6764
|
const { call_cid } = event;
|
|
6433
6765
|
if (!call_cid) {
|
|
6434
6766
|
console.warn("Can't find call_cid in CallRejectedEvent");
|
|
@@ -6462,6 +6794,9 @@ const watchCallRejected = (store) => {
|
|
|
6462
6794
|
*/
|
|
6463
6795
|
const watchCallCancelled = (store) => {
|
|
6464
6796
|
return function onCallCancelled(event) {
|
|
6797
|
+
if (event.type !== 'call.ended') {
|
|
6798
|
+
return;
|
|
6799
|
+
}
|
|
6465
6800
|
const { call_cid } = event;
|
|
6466
6801
|
if (!call_cid) {
|
|
6467
6802
|
console.log("Can't find call in CallEndedEvent");
|
|
@@ -6488,6 +6823,9 @@ const watchCallCancelled = (store) => {
|
|
|
6488
6823
|
*/
|
|
6489
6824
|
const watchCallPermissionRequest = (store) => {
|
|
6490
6825
|
return function onCallPermissionRequest(event) {
|
|
6826
|
+
if (event.type !== 'call.permission_request') {
|
|
6827
|
+
return;
|
|
6828
|
+
}
|
|
6491
6829
|
const activeCall = store.getCurrentValue(store.activeCallSubject);
|
|
6492
6830
|
if (!activeCall) {
|
|
6493
6831
|
console.warn(`Ignoring "call.permission_request" as there is no active call`, event);
|
|
@@ -6512,6 +6850,9 @@ const watchCallPermissionRequest = (store) => {
|
|
|
6512
6850
|
*/
|
|
6513
6851
|
const watchCallPermissionsUpdated = (store) => {
|
|
6514
6852
|
return function onCallPermissionsUpdated(event) {
|
|
6853
|
+
if (event.type !== 'call.permissions_updated') {
|
|
6854
|
+
return;
|
|
6855
|
+
}
|
|
6515
6856
|
const activeCall = store.getCurrentValue(store.activeCallSubject);
|
|
6516
6857
|
if (!activeCall) {
|
|
6517
6858
|
console.warn(`Ignoring "call.permissions_updated" as there is no active call`, event);
|
|
@@ -6539,6 +6880,9 @@ const watchCallPermissionsUpdated = (store) => {
|
|
|
6539
6880
|
*/
|
|
6540
6881
|
const watchNewReactions = (store) => {
|
|
6541
6882
|
return function onNewReactions(event) {
|
|
6883
|
+
if (event.type !== 'call.reaction_new') {
|
|
6884
|
+
return;
|
|
6885
|
+
}
|
|
6542
6886
|
const { call_cid, reaction } = event;
|
|
6543
6887
|
const activeCall = store.getCurrentValue(store.activeCallSubject);
|
|
6544
6888
|
if (!activeCall || activeCall.cid !== call_cid) {
|
|
@@ -6571,6 +6915,9 @@ const watchNewReactions = (store) => {
|
|
|
6571
6915
|
*/
|
|
6572
6916
|
const watchCallRecordingStarted = (store) => {
|
|
6573
6917
|
return function onCallRecordingStarted(event) {
|
|
6918
|
+
if (event.type !== 'call.recording_started') {
|
|
6919
|
+
return;
|
|
6920
|
+
}
|
|
6574
6921
|
const { call_cid } = event;
|
|
6575
6922
|
const activeCall = store.getCurrentValue(store.activeCallSubject);
|
|
6576
6923
|
if (!activeCall || activeCall.cid !== call_cid) {
|
|
@@ -6586,6 +6933,9 @@ const watchCallRecordingStarted = (store) => {
|
|
|
6586
6933
|
*/
|
|
6587
6934
|
const watchCallRecordingStopped = (store) => {
|
|
6588
6935
|
return function onCallRecordingStopped(event) {
|
|
6936
|
+
if (event.type !== 'call.recording_stopped') {
|
|
6937
|
+
return;
|
|
6938
|
+
}
|
|
6589
6939
|
const { call_cid } = event;
|
|
6590
6940
|
const activeCall = store.getCurrentValue(store.activeCallSubject);
|
|
6591
6941
|
if (!activeCall || activeCall.cid !== call_cid) {
|
|
@@ -6603,6 +6953,9 @@ const watchCallRecordingStopped = (store) => {
|
|
|
6603
6953
|
* `event.user_id` to the list
|
|
6604
6954
|
*/
|
|
6605
6955
|
const watchBlockedUser = (store) => (event) => {
|
|
6956
|
+
if (event.type !== 'call.blocked_user') {
|
|
6957
|
+
return;
|
|
6958
|
+
}
|
|
6606
6959
|
const activeCall = store.getCurrentValue(store.activeCallSubject);
|
|
6607
6960
|
if (!activeCall || activeCall.cid !== event.call_cid) {
|
|
6608
6961
|
console.warn(`Received "call.blocked_user" for an inactive or unknown call`, event);
|
|
@@ -6622,6 +6975,9 @@ const watchBlockedUser = (store) => (event) => {
|
|
|
6622
6975
|
* removing `event.user_id` from the list
|
|
6623
6976
|
*/
|
|
6624
6977
|
const watchUnblockedUser = (store) => (event) => {
|
|
6978
|
+
if (event.type !== 'call.unblocked_user') {
|
|
6979
|
+
return;
|
|
6980
|
+
}
|
|
6625
6981
|
const activeCall = store.getCurrentValue(store.activeCallSubject);
|
|
6626
6982
|
if (!activeCall || activeCall.cid !== event.call_cid) {
|
|
6627
6983
|
console.warn(`Received "call.unblocked_user" for an inactive or unknown call`, event);
|
|
@@ -6762,129 +7118,6 @@ CallDropScheduler.getLatestCall = (from, compareTo) => {
|
|
|
6762
7118
|
|
|
6763
7119
|
var https = null;
|
|
6764
7120
|
|
|
6765
|
-
const sleep = (m) => new Promise((r) => setTimeout(r, m));
|
|
6766
|
-
function isFunction(value) {
|
|
6767
|
-
return (value &&
|
|
6768
|
-
(Object.prototype.toString.call(value) === '[object Function]' ||
|
|
6769
|
-
'function' === typeof value ||
|
|
6770
|
-
value instanceof Function));
|
|
6771
|
-
}
|
|
6772
|
-
// todo: rename so that it does not contain word "chat"
|
|
6773
|
-
const chatCodes = {
|
|
6774
|
-
TOKEN_EXPIRED: 40,
|
|
6775
|
-
WS_CLOSED_SUCCESS: 1000,
|
|
6776
|
-
};
|
|
6777
|
-
/**
|
|
6778
|
-
* retryInterval - A retry interval which increases acc to number of failures
|
|
6779
|
-
*
|
|
6780
|
-
* @return {number} Duration to wait in milliseconds
|
|
6781
|
-
*/
|
|
6782
|
-
function retryInterval(numberOfFailures) {
|
|
6783
|
-
// try to reconnect in 0.25-25 seconds (random to spread out the load from failures)
|
|
6784
|
-
const max = Math.min(500 + numberOfFailures * 2000, 25000);
|
|
6785
|
-
const min = Math.min(Math.max(250, (numberOfFailures - 1) * 2000), 25000);
|
|
6786
|
-
return Math.floor(Math.random() * (max - min) + min);
|
|
6787
|
-
}
|
|
6788
|
-
function randomId() {
|
|
6789
|
-
return generateUUIDv4();
|
|
6790
|
-
}
|
|
6791
|
-
function hex(bytes) {
|
|
6792
|
-
let s = '';
|
|
6793
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
6794
|
-
s += bytes[i].toString(16).padStart(2, '0');
|
|
6795
|
-
}
|
|
6796
|
-
return s;
|
|
6797
|
-
}
|
|
6798
|
-
// https://tools.ietf.org/html/rfc4122
|
|
6799
|
-
function generateUUIDv4() {
|
|
6800
|
-
const bytes = getRandomBytes(16);
|
|
6801
|
-
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version
|
|
6802
|
-
bytes[8] = (bytes[8] & 0xbf) | 0x80; // variant
|
|
6803
|
-
return (hex(bytes.subarray(0, 4)) +
|
|
6804
|
-
'-' +
|
|
6805
|
-
hex(bytes.subarray(4, 6)) +
|
|
6806
|
-
'-' +
|
|
6807
|
-
hex(bytes.subarray(6, 8)) +
|
|
6808
|
-
'-' +
|
|
6809
|
-
hex(bytes.subarray(8, 10)) +
|
|
6810
|
-
'-' +
|
|
6811
|
-
hex(bytes.subarray(10, 16)));
|
|
6812
|
-
}
|
|
6813
|
-
function getRandomValuesWithMathRandom(bytes) {
|
|
6814
|
-
const max = Math.pow(2, (8 * bytes.byteLength) / bytes.length);
|
|
6815
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
6816
|
-
bytes[i] = Math.random() * max;
|
|
6817
|
-
}
|
|
6818
|
-
}
|
|
6819
|
-
const getRandomValues = (() => {
|
|
6820
|
-
if (typeof crypto !== 'undefined' &&
|
|
6821
|
-
typeof (crypto === null || crypto === void 0 ? void 0 : crypto.getRandomValues) !== 'undefined') {
|
|
6822
|
-
return crypto.getRandomValues.bind(crypto);
|
|
6823
|
-
}
|
|
6824
|
-
else if (typeof msCrypto !== 'undefined') {
|
|
6825
|
-
return msCrypto.getRandomValues.bind(msCrypto);
|
|
6826
|
-
}
|
|
6827
|
-
else {
|
|
6828
|
-
return getRandomValuesWithMathRandom;
|
|
6829
|
-
}
|
|
6830
|
-
})();
|
|
6831
|
-
function getRandomBytes(length) {
|
|
6832
|
-
const bytes = new Uint8Array(length);
|
|
6833
|
-
getRandomValues(bytes);
|
|
6834
|
-
return bytes;
|
|
6835
|
-
}
|
|
6836
|
-
function convertErrorToJson(err) {
|
|
6837
|
-
const jsonObj = {};
|
|
6838
|
-
if (!err)
|
|
6839
|
-
return jsonObj;
|
|
6840
|
-
try {
|
|
6841
|
-
Object.getOwnPropertyNames(err).forEach((key) => {
|
|
6842
|
-
jsonObj[key] = Object.getOwnPropertyDescriptor(err, key);
|
|
6843
|
-
});
|
|
6844
|
-
}
|
|
6845
|
-
catch (_) {
|
|
6846
|
-
return {
|
|
6847
|
-
error: 'failed to serialize the error',
|
|
6848
|
-
};
|
|
6849
|
-
}
|
|
6850
|
-
return jsonObj;
|
|
6851
|
-
}
|
|
6852
|
-
/**
|
|
6853
|
-
* isOnline safely return the navigator.online value for browser env
|
|
6854
|
-
* if navigator is not in global object, it always return true
|
|
6855
|
-
*/
|
|
6856
|
-
function isOnline() {
|
|
6857
|
-
const nav = typeof navigator !== 'undefined'
|
|
6858
|
-
? navigator
|
|
6859
|
-
: typeof window !== 'undefined' && window.navigator
|
|
6860
|
-
? window.navigator
|
|
6861
|
-
: undefined;
|
|
6862
|
-
if (!nav) {
|
|
6863
|
-
console.warn('isOnline failed to access window.navigator and assume browser is online');
|
|
6864
|
-
return true;
|
|
6865
|
-
}
|
|
6866
|
-
// RN navigator has undefined for onLine
|
|
6867
|
-
if (typeof nav.onLine !== 'boolean') {
|
|
6868
|
-
return true;
|
|
6869
|
-
}
|
|
6870
|
-
return nav.onLine;
|
|
6871
|
-
}
|
|
6872
|
-
/**
|
|
6873
|
-
* listenForConnectionChanges - Adds an event listener fired on browser going online or offline
|
|
6874
|
-
*/
|
|
6875
|
-
function addConnectionEventListeners(cb) {
|
|
6876
|
-
if (typeof window !== 'undefined' && window.addEventListener) {
|
|
6877
|
-
window.addEventListener('offline', cb);
|
|
6878
|
-
window.addEventListener('online', cb);
|
|
6879
|
-
}
|
|
6880
|
-
}
|
|
6881
|
-
function removeConnectionEventListeners(cb) {
|
|
6882
|
-
if (typeof window !== 'undefined' && window.removeEventListener) {
|
|
6883
|
-
window.removeEventListener('offline', cb);
|
|
6884
|
-
window.removeEventListener('online', cb);
|
|
6885
|
-
}
|
|
6886
|
-
}
|
|
6887
|
-
|
|
6888
7121
|
class InsightMetrics {
|
|
6889
7122
|
constructor() {
|
|
6890
7123
|
this.connectionStartTimestamp = null;
|
|
@@ -8495,7 +8728,7 @@ class StreamClient {
|
|
|
8495
8728
|
}
|
|
8496
8729
|
getUserAgent() {
|
|
8497
8730
|
return (this.userAgent ||
|
|
8498
|
-
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.1-alpha.
|
|
8731
|
+
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.1-alpha.82"}`);
|
|
8499
8732
|
}
|
|
8500
8733
|
setUserAgent(userAgent) {
|
|
8501
8734
|
this.userAgent = userAgent;
|
|
@@ -8556,39 +8789,17 @@ class StreamVideoClient {
|
|
|
8556
8789
|
// @ts-expect-error
|
|
8557
8790
|
user, tokenOrProvider);
|
|
8558
8791
|
this.callDropScheduler = new CallDropScheduler(this.writeableStateStore, this.callConfig);
|
|
8559
|
-
this.on('call.created',
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
this.on('call.
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
this.on('call.
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
this.on('call.
|
|
8569
|
-
|
|
8570
|
-
watchCallCancelled(this.writeableStateStore));
|
|
8571
|
-
this.on('call.permission_request',
|
|
8572
|
-
// @ts-expect-error until we sort out the types
|
|
8573
|
-
watchCallPermissionRequest(this.writeableStateStore));
|
|
8574
|
-
this.on('call.permissions_updated',
|
|
8575
|
-
// @ts-expect-error until we sort out the types
|
|
8576
|
-
watchCallPermissionsUpdated(this.writeableStateStore));
|
|
8577
|
-
this.on('call.blocked_user',
|
|
8578
|
-
// @ts-expect-error until we sort out the types
|
|
8579
|
-
watchBlockedUser(this.writeableStateStore));
|
|
8580
|
-
this.on('call.unblocked_user',
|
|
8581
|
-
// @ts-expect-error until we sort out the types
|
|
8582
|
-
watchUnblockedUser(this.writeableStateStore));
|
|
8583
|
-
this.on('call.recording_started',
|
|
8584
|
-
// @ts-expect-error until we sort out the types
|
|
8585
|
-
watchCallRecordingStarted(this.writeableStateStore));
|
|
8586
|
-
this.on('call.recording_stopped',
|
|
8587
|
-
// @ts-expect-error until we sort out the types
|
|
8588
|
-
watchCallRecordingStopped(this.writeableStateStore));
|
|
8589
|
-
this.on('call.reaction_new',
|
|
8590
|
-
// @ts-expect-error until we sort out the types
|
|
8591
|
-
watchNewReactions(this.writeableStateStore));
|
|
8792
|
+
this.on('call.created', watchCallCreated(this.writeableStateStore, this.streamClient));
|
|
8793
|
+
this.on('call.accepted', watchCallAccepted(this.writeableStateStore));
|
|
8794
|
+
this.on('call.rejected', watchCallRejected(this.writeableStateStore));
|
|
8795
|
+
this.on('call.ended', watchCallCancelled(this.writeableStateStore));
|
|
8796
|
+
this.on('call.permission_request', watchCallPermissionRequest(this.writeableStateStore));
|
|
8797
|
+
this.on('call.permissions_updated', watchCallPermissionsUpdated(this.writeableStateStore));
|
|
8798
|
+
this.on('call.blocked_user', watchBlockedUser(this.writeableStateStore));
|
|
8799
|
+
this.on('call.unblocked_user', watchUnblockedUser(this.writeableStateStore));
|
|
8800
|
+
this.on('call.recording_started', watchCallRecordingStarted(this.writeableStateStore));
|
|
8801
|
+
this.on('call.recording_stopped', watchCallRecordingStopped(this.writeableStateStore));
|
|
8802
|
+
this.on('call.reaction_new', watchNewReactions(this.writeableStateStore));
|
|
8592
8803
|
this.writeableStateStore.setCurrentValue(this.writeableStateStore.connectedUserSubject, user);
|
|
8593
8804
|
});
|
|
8594
8805
|
/**
|
|
@@ -9045,5 +9256,5 @@ const createSoundDetector = (audioStream, onSoundDetectedStateChanged, options =
|
|
|
9045
9256
|
};
|
|
9046
9257
|
};
|
|
9047
9258
|
|
|
9048
|
-
export { browsers as Browsers, CALL_CONFIG, Call, CallState, CallType, CallTypes, DeviceFieldsRequestPushProviderEnum, ErrorFromResponse, OwnCapability, RecordSettingsModeEnum, RecordSettingsQualityEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, events as SfuEvents, models as SfuModels, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsModeEnum, TranscriptionSettingsRequestModeEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, disposeOfMediaStream, dominantSpeaker, getAudioDevices, getAudioOutputDevices, getAudioStream, getScreenShareStream, getVideoDevices, getVideoStream, isStreamVideoLocalParticipant, livestreamOrAudioRoomSortPreset, name, noopComparator, pinned, publishingAudio, publishingVideo, reactionType, role, screenSharing, speakerLayoutSortPreset, speaking, watchForAddedDefaultAudioDevice, watchForAddedDefaultAudioOutputDevice, watchForAddedDefaultVideoDevice, watchForDisconnectedAudioDevice, watchForDisconnectedAudioOutputDevice, watchForDisconnectedVideoDevice };
|
|
9259
|
+
export { browsers as Browsers, CALL_CONFIG, Call, CallState, CallType, CallTypes, CallingState, DeviceFieldsRequestPushProviderEnum, ErrorFromResponse, OwnCapability, RecordSettingsModeEnum, RecordSettingsQualityEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, events as SfuEvents, models as SfuModels, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsModeEnum, TranscriptionSettingsRequestModeEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, disposeOfMediaStream, dominantSpeaker, getAudioDevices, getAudioOutputDevices, getAudioStream, getScreenShareStream, getVideoDevices, getVideoStream, isStreamVideoLocalParticipant, livestreamOrAudioRoomSortPreset, name, noopComparator, pinned, publishingAudio, publishingVideo, reactionType, role, screenSharing, speakerLayoutSortPreset, speaking, watchForAddedDefaultAudioDevice, watchForAddedDefaultAudioOutputDevice, watchForAddedDefaultVideoDevice, watchForDisconnectedAudioDevice, watchForDisconnectedAudioOutputDevice, watchForDisconnectedVideoDevice };
|
|
9049
9260
|
//# sourceMappingURL=index.browser.es.js.map
|