@stream-io/video-client 1.58.0 → 1.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/index.browser.es.js +319 -71
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +329 -69
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.es.js +319 -71
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +15 -4
- package/dist/src/StreamVideoClient.d.ts +9 -1
- package/dist/src/devices/CameraManager.d.ts +2 -1
- package/dist/src/devices/DeviceManager.d.ts +1 -1
- package/dist/src/devices/MicrophoneManager.d.ts +2 -1
- package/dist/src/gen/coordinator/index.d.ts +26 -0
- package/dist/src/helpers/MediaStreamRecorder.d.ts +26 -0
- package/dist/src/helpers/concurrency.d.ts +8 -0
- package/dist/src/helpers/loopback.d.ts +55 -0
- package/dist/src/reporting/ClientEventReporter.d.ts +0 -2
- package/dist/src/rtc/Subscriber.d.ts +5 -0
- package/index.ts +2 -0
- package/package.json +1 -1
- package/src/Call.ts +156 -134
- package/src/StreamVideoClient.ts +35 -8
- package/src/__tests__/Call.lifecycle.test.ts +33 -2
- package/src/__tests__/Call.publishing.test.ts +42 -1
- package/src/__tests__/StreamVideoClient.api.test.ts +33 -0
- package/src/__tests__/StreamVideoClient.rewatch.test.ts +213 -0
- package/src/__tests__/StreamVideoClient.ringing.test.ts +11 -4
- package/src/__tests__/StreamVideoClient.test.ts +14 -12
- package/src/devices/CameraManager.ts +16 -4
- package/src/devices/DeviceManager.ts +7 -4
- package/src/devices/MicrophoneManager.ts +11 -3
- package/src/devices/__tests__/CameraManager.test.ts +27 -1
- package/src/devices/__tests__/DeviceManager.test.ts +49 -1
- package/src/devices/__tests__/MicrophoneManager.test.ts +23 -1
- package/src/events/call.ts +6 -3
- package/src/gen/coordinator/index.ts +26 -0
- package/src/helpers/MediaStreamRecorder.ts +104 -0
- package/src/helpers/__tests__/MediaStreamRecorder.test.ts +324 -0
- package/src/helpers/__tests__/concurrency.test.ts +74 -0
- package/src/helpers/__tests__/loopback.test.ts +367 -0
- package/src/helpers/concurrency.ts +24 -0
- package/src/helpers/loopback.ts +200 -0
- package/src/reporting/ClientEventReporter.ts +15 -28
- package/src/reporting/__tests__/ClientEventReporter.test.ts +3 -7
- package/src/rtc/Subscriber.ts +8 -0
- package/src/rtc/__tests__/Subscriber.test.ts +101 -0
package/dist/index.browser.es.js
CHANGED
|
@@ -7,7 +7,7 @@ import { TwirpFetchTransport, TwirpErrorCode } from '@protobuf-ts/twirp-transpor
|
|
|
7
7
|
import * as scopedLogger from '@stream-io/logger';
|
|
8
8
|
export { LogLevelEnum } from '@stream-io/logger';
|
|
9
9
|
import { parse, write } from 'sdp-transform';
|
|
10
|
-
import { Subject, Observable, combineLatest, BehaviorSubject, ReplaySubject, shareReplay, map, distinctUntilChanged, startWith, takeWhile, distinctUntilKeyChanged, fromEventPattern, concatMap, merge, from, fromEvent, tap, debounceTime, pairwise, firstValueFrom, of } from 'rxjs';
|
|
10
|
+
import { Subject, Observable, combineLatest, BehaviorSubject, ReplaySubject, shareReplay, map, distinctUntilChanged, startWith, takeWhile, distinctUntilKeyChanged, fromEventPattern, concatMap, merge, from, fromEvent, tap, debounceTime, pairwise, firstValueFrom, of, Subscription } from 'rxjs';
|
|
11
11
|
import { UAParser } from 'ua-parser-js';
|
|
12
12
|
import { WorkerTimer } from '@stream-io/worker-timer';
|
|
13
13
|
|
|
@@ -5059,7 +5059,7 @@ const toIceCandidate = (iceTrickle) => {
|
|
|
5059
5059
|
}
|
|
5060
5060
|
};
|
|
5061
5061
|
|
|
5062
|
-
const version = "1.
|
|
5062
|
+
const version = "1.59.0";
|
|
5063
5063
|
const [major, minor, patch] = version.split('.');
|
|
5064
5064
|
let sdkInfo = {
|
|
5065
5065
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -5316,6 +5316,27 @@ const withoutConcurrency = createRunner(wrapWithContinuationTracking);
|
|
|
5316
5316
|
*/
|
|
5317
5317
|
const withCancellation = createRunner(wrapWithCancellation);
|
|
5318
5318
|
const pendingPromises = new Map();
|
|
5319
|
+
/**
|
|
5320
|
+
* Wraps an async function so concurrent calls share the in-flight promise
|
|
5321
|
+
* instead of queueing another execution.
|
|
5322
|
+
*
|
|
5323
|
+
* @param cb Async function to wrap.
|
|
5324
|
+
* @returns Wrapped async function.
|
|
5325
|
+
*/
|
|
5326
|
+
function singleFlight(cb) {
|
|
5327
|
+
let pending;
|
|
5328
|
+
return (...args) => {
|
|
5329
|
+
if (pending)
|
|
5330
|
+
return pending;
|
|
5331
|
+
const promise = cb(...args).finally(() => {
|
|
5332
|
+
if (pending === promise) {
|
|
5333
|
+
pending = undefined;
|
|
5334
|
+
}
|
|
5335
|
+
});
|
|
5336
|
+
pending = promise;
|
|
5337
|
+
return promise;
|
|
5338
|
+
};
|
|
5339
|
+
}
|
|
5319
5340
|
function hasPending(tag) {
|
|
5320
5341
|
return pendingPromises.has(tag);
|
|
5321
5342
|
}
|
|
@@ -9804,6 +9825,11 @@ class Subscriber extends BasePeerConnection {
|
|
|
9804
9825
|
throw e;
|
|
9805
9826
|
}
|
|
9806
9827
|
};
|
|
9828
|
+
/**
|
|
9829
|
+
* Whether the given stream is one the SFU echoed back to us
|
|
9830
|
+
* (`allowOwnTracksLoopback`) rather than a local capture stream.
|
|
9831
|
+
*/
|
|
9832
|
+
isSelfSubscribedStream = (stream) => !!stream && !!this.trackedStreams?.has(stream);
|
|
9807
9833
|
handleOnTrack = (e) => {
|
|
9808
9834
|
const { streams, track } = e;
|
|
9809
9835
|
const [primaryStream] = streams;
|
|
@@ -9893,6 +9919,8 @@ class Subscriber extends BasePeerConnection {
|
|
|
9893
9919
|
const target = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
|
|
9894
9920
|
if (!target)
|
|
9895
9921
|
return;
|
|
9922
|
+
if (target.isLocalParticipant)
|
|
9923
|
+
return;
|
|
9896
9924
|
this.state.updateParticipant(target.sessionId, (p) => {
|
|
9897
9925
|
const current = p.interruptedTracks ?? [];
|
|
9898
9926
|
const has = current.includes(trackType);
|
|
@@ -10635,7 +10663,6 @@ const watchCallRejected = (call) => {
|
|
|
10635
10663
|
*/
|
|
10636
10664
|
const watchCallEnded = (call) => {
|
|
10637
10665
|
return function onCallEnded() {
|
|
10638
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(call, 'remote');
|
|
10639
10666
|
const { callingState } = call.state;
|
|
10640
10667
|
if (callingState !== CallingState.IDLE &&
|
|
10641
10668
|
callingState !== CallingState.LEFT) {
|
|
@@ -10644,9 +10671,13 @@ const watchCallEnded = (call) => {
|
|
|
10644
10671
|
reason: 'call.ended event received',
|
|
10645
10672
|
});
|
|
10646
10673
|
call
|
|
10647
|
-
.leave({
|
|
10674
|
+
.leave({
|
|
10675
|
+
message: 'call.ended event received',
|
|
10676
|
+
reject: false,
|
|
10677
|
+
reason: 'ended',
|
|
10678
|
+
})
|
|
10648
10679
|
.catch((err) => {
|
|
10649
|
-
call.logger.error('Failed to leave call after call.ended
|
|
10680
|
+
call.logger.error('Failed to leave call after call.ended', err);
|
|
10650
10681
|
});
|
|
10651
10682
|
}
|
|
10652
10683
|
};
|
|
@@ -13452,7 +13483,7 @@ class DeviceManager {
|
|
|
13452
13483
|
createSyntheticDevice(selectedDevice, deviceKind);
|
|
13453
13484
|
writePreferences(currentDevice, deviceKey, muted, storageKey);
|
|
13454
13485
|
}
|
|
13455
|
-
async applyPersistedPreferences(enabledInCallType) {
|
|
13486
|
+
async applyPersistedPreferences(enabledInCallType, forceDisabled = false) {
|
|
13456
13487
|
const deviceKey = this.trackType === TrackType.AUDIO ? 'microphone' : 'camera';
|
|
13457
13488
|
const preferences = readPreferences(this.devicePersistence.storageKey);
|
|
13458
13489
|
const preferenceList = toPreferenceList(preferences[deviceKey]);
|
|
@@ -13479,13 +13510,13 @@ class DeviceManager {
|
|
|
13479
13510
|
}
|
|
13480
13511
|
const canPublish = this.call.permissionsContext.canPublish(this.trackType);
|
|
13481
13512
|
if (typeof muted === 'boolean' && enabledInCallType && canPublish) {
|
|
13482
|
-
await this.applyMutedState(muted);
|
|
13513
|
+
await this.applyMutedState(muted, forceDisabled);
|
|
13483
13514
|
appliedMute = true;
|
|
13484
13515
|
}
|
|
13485
13516
|
return appliedDevice || appliedMute;
|
|
13486
13517
|
}
|
|
13487
|
-
async applyMutedState(muted) {
|
|
13488
|
-
if (this.state.status !== undefined)
|
|
13518
|
+
async applyMutedState(muted, forceDisabled) {
|
|
13519
|
+
if (forceDisabled || this.state.status !== undefined)
|
|
13489
13520
|
return;
|
|
13490
13521
|
if (muted) {
|
|
13491
13522
|
await this.disable();
|
|
@@ -13837,8 +13868,9 @@ class CameraManager extends DeviceManager {
|
|
|
13837
13868
|
*
|
|
13838
13869
|
* @param settings the video settings to apply.
|
|
13839
13870
|
* @param publish whether to publish the stream after applying the settings.
|
|
13871
|
+
* @param forceDisabled whether to keep the camera disabled, regardless of server-side or local preferences.
|
|
13840
13872
|
*/
|
|
13841
|
-
async apply(settings, publish) {
|
|
13873
|
+
async apply(settings, publish, forceDisabled = false) {
|
|
13842
13874
|
// Wait for any in progress camera operation
|
|
13843
13875
|
await this.statusChangeSettled();
|
|
13844
13876
|
await this.selectTargetResolution(settings.target_resolution);
|
|
@@ -13850,8 +13882,7 @@ class CameraManager extends DeviceManager {
|
|
|
13850
13882
|
if (shouldApplyDefaults &&
|
|
13851
13883
|
this.devicePersistence.enabled &&
|
|
13852
13884
|
permissionState === 'granted') {
|
|
13853
|
-
persistedPreferencesApplied =
|
|
13854
|
-
await this.applyPersistedPreferences(enabledInCallType);
|
|
13885
|
+
persistedPreferencesApplied = await this.applyPersistedPreferences(enabledInCallType, forceDisabled);
|
|
13855
13886
|
}
|
|
13856
13887
|
// apply a direction and enable the camera only if in "pristine" state,
|
|
13857
13888
|
// and there are no persisted preferences
|
|
@@ -13861,7 +13892,10 @@ class CameraManager extends DeviceManager {
|
|
|
13861
13892
|
const direction = settings.camera_facing === 'front' ? 'front' : 'back';
|
|
13862
13893
|
await this.selectDirection(direction, { enableCamera: false });
|
|
13863
13894
|
}
|
|
13864
|
-
if (
|
|
13895
|
+
if (!forceDisabled &&
|
|
13896
|
+
canPublish &&
|
|
13897
|
+
settings.camera_default_on &&
|
|
13898
|
+
enabledInCallType) {
|
|
13865
13899
|
await this.enable();
|
|
13866
13900
|
}
|
|
13867
13901
|
}
|
|
@@ -14456,8 +14490,9 @@ class MicrophoneManager extends AudioDeviceManager {
|
|
|
14456
14490
|
* Applies the audio settings to the microphone.
|
|
14457
14491
|
* @param settings the audio settings to apply.
|
|
14458
14492
|
* @param publish whether to publish the stream after applying the settings.
|
|
14493
|
+
* @param forceDisabled whether to keep the mic disabled, regardless of server-side or local preferences.
|
|
14459
14494
|
*/
|
|
14460
|
-
async apply(settings, publish) {
|
|
14495
|
+
async apply(settings, publish, forceDisabled = false) {
|
|
14461
14496
|
// Wait for any in progress mic operation
|
|
14462
14497
|
await this.statusChangeSettled();
|
|
14463
14498
|
// apply server-side settings only when the device state is pristine
|
|
@@ -14469,11 +14504,11 @@ class MicrophoneManager extends AudioDeviceManager {
|
|
|
14469
14504
|
if (shouldApplyDefaults &&
|
|
14470
14505
|
this.devicePersistence.enabled &&
|
|
14471
14506
|
permissionState === 'granted') {
|
|
14472
|
-
persistedPreferencesApplied = await this.applyPersistedPreferences(true);
|
|
14507
|
+
persistedPreferencesApplied = await this.applyPersistedPreferences(true, forceDisabled);
|
|
14473
14508
|
}
|
|
14474
14509
|
const canPublish = this.call.permissionsContext.canPublish(this.trackType);
|
|
14475
14510
|
if (shouldApplyDefaults && !persistedPreferencesApplied) {
|
|
14476
|
-
if (canPublish && settings.mic_default_on) {
|
|
14511
|
+
if (!forceDisabled && canPublish && settings.mic_default_on) {
|
|
14477
14512
|
await this.enable();
|
|
14478
14513
|
}
|
|
14479
14514
|
}
|
|
@@ -15442,7 +15477,7 @@ class Call {
|
|
|
15442
15477
|
}
|
|
15443
15478
|
}
|
|
15444
15479
|
}
|
|
15445
|
-
globalThis.streamRNVideoSDK?.callingX?.endCall(this);
|
|
15480
|
+
globalThis.streamRNVideoSDK?.callingX?.endCall(this, reason === 'ended' ? 'remote' : undefined);
|
|
15446
15481
|
this.statsReporter?.stop();
|
|
15447
15482
|
this.statsReporter = undefined;
|
|
15448
15483
|
const leaveReason = message ?? reason ?? 'user is leaving the call';
|
|
@@ -15605,8 +15640,18 @@ class Call {
|
|
|
15605
15640
|
// const calls = useCalls().filter((c) => c.ringing);
|
|
15606
15641
|
const calls = this.clientStore.calls.filter((c) => c.cid !== this.cid);
|
|
15607
15642
|
this.clientStore.setCalls([this, ...calls]);
|
|
15608
|
-
|
|
15609
|
-
|
|
15643
|
+
await this.applyDeviceConfig(settings, { publish: false });
|
|
15644
|
+
};
|
|
15645
|
+
/**
|
|
15646
|
+
* Applies a backend-provided call state snapshot
|
|
15647
|
+
* (call, members, own capabilities) to this instance.
|
|
15648
|
+
*
|
|
15649
|
+
* @internal an internal method and should not be used outside the SDK.
|
|
15650
|
+
*/
|
|
15651
|
+
updateFromCallStateResponse = (response) => {
|
|
15652
|
+
this.state.updateFromCallResponse(response.call);
|
|
15653
|
+
this.state.setMembers(response.members);
|
|
15654
|
+
this.state.setOwnCapabilities(response.own_capabilities);
|
|
15610
15655
|
};
|
|
15611
15656
|
/**
|
|
15612
15657
|
* Loads the information about the call.
|
|
@@ -15619,9 +15664,7 @@ class Call {
|
|
|
15619
15664
|
get = async (params) => {
|
|
15620
15665
|
await this.setup();
|
|
15621
15666
|
const response = await this.streamClient.get(this.streamClientBasePath, params);
|
|
15622
|
-
this.
|
|
15623
|
-
this.state.setMembers(response.members);
|
|
15624
|
-
this.state.setOwnCapabilities(response.own_capabilities);
|
|
15667
|
+
this.updateFromCallStateResponse(response);
|
|
15625
15668
|
if (params?.ring) {
|
|
15626
15669
|
this.ringingSubject.next(true);
|
|
15627
15670
|
}
|
|
@@ -15629,9 +15672,7 @@ class Call {
|
|
|
15629
15672
|
this.watching = true;
|
|
15630
15673
|
this.clientStore.registerOrUpdateCall(this);
|
|
15631
15674
|
}
|
|
15632
|
-
|
|
15633
|
-
const skipSpeakerApply = isReactNative();
|
|
15634
|
-
await this.applyDeviceConfig(response.call.settings, false, skipSpeakerApply);
|
|
15675
|
+
await this.applyDeviceConfig(response.call.settings, { publish: false });
|
|
15635
15676
|
return response;
|
|
15636
15677
|
};
|
|
15637
15678
|
/**
|
|
@@ -15642,9 +15683,7 @@ class Call {
|
|
|
15642
15683
|
getOrCreate = async (data) => {
|
|
15643
15684
|
await this.setup();
|
|
15644
15685
|
const response = await this.streamClient.post(this.streamClientBasePath, data);
|
|
15645
|
-
this.
|
|
15646
|
-
this.state.setMembers(response.members);
|
|
15647
|
-
this.state.setOwnCapabilities(response.own_capabilities);
|
|
15686
|
+
this.updateFromCallStateResponse(response);
|
|
15648
15687
|
if (data?.ring) {
|
|
15649
15688
|
this.ringingSubject.next(true);
|
|
15650
15689
|
}
|
|
@@ -15652,9 +15691,7 @@ class Call {
|
|
|
15652
15691
|
this.watching = true;
|
|
15653
15692
|
this.clientStore.registerOrUpdateCall(this);
|
|
15654
15693
|
}
|
|
15655
|
-
|
|
15656
|
-
const skipSpeakerApply = isReactNative();
|
|
15657
|
-
await this.applyDeviceConfig(response.call.settings, false, skipSpeakerApply);
|
|
15694
|
+
await this.applyDeviceConfig(response.call.settings, { publish: false });
|
|
15658
15695
|
return response;
|
|
15659
15696
|
};
|
|
15660
15697
|
/**
|
|
@@ -15718,7 +15755,7 @@ class Call {
|
|
|
15718
15755
|
*
|
|
15719
15756
|
* @returns a promise which resolves once the call join-flow has finished.
|
|
15720
15757
|
*/
|
|
15721
|
-
join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, allowOwnTracksLoopback = false, ...data } = {}) => {
|
|
15758
|
+
join = singleFlight(async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, allowOwnTracksLoopback = false, ...data } = {}) => {
|
|
15722
15759
|
const callingState = this.state.callingState;
|
|
15723
15760
|
if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
|
|
15724
15761
|
throw new Error(`Illegal State: call.join() shall be called only once`);
|
|
@@ -15740,7 +15777,6 @@ class Call {
|
|
|
15740
15777
|
callId: this.id,
|
|
15741
15778
|
getCallSessionId: () => this.state.session?.id ?? '',
|
|
15742
15779
|
getSfuId: () => this.credentials?.server.edge_name ?? '',
|
|
15743
|
-
getUserSessionId: () => this.sfuClient?.sessionId ?? '',
|
|
15744
15780
|
});
|
|
15745
15781
|
this.joinResponseTimeout = joinResponseTimeout;
|
|
15746
15782
|
this.rpcRequestTimeout = rpcRequestTimeout;
|
|
@@ -15792,7 +15828,7 @@ class Call {
|
|
|
15792
15828
|
callingX?.endCall(this, 'error');
|
|
15793
15829
|
throw error;
|
|
15794
15830
|
}
|
|
15795
|
-
};
|
|
15831
|
+
});
|
|
15796
15832
|
/**
|
|
15797
15833
|
* Will make a single attempt to watch for call related WebSocket events
|
|
15798
15834
|
* and initiate a call session with the server.
|
|
@@ -15964,7 +16000,10 @@ class Call {
|
|
|
15964
16000
|
if (!this.deviceSettingsAppliedOnce &&
|
|
15965
16001
|
this.state.settings &&
|
|
15966
16002
|
!supersededByLeave()) {
|
|
15967
|
-
await this.applyDeviceConfig(this.state.settings,
|
|
16003
|
+
await this.applyDeviceConfig(this.state.settings, {
|
|
16004
|
+
publish: true,
|
|
16005
|
+
skipSpeakerApply: false,
|
|
16006
|
+
});
|
|
15968
16007
|
this.deviceSettingsAppliedOnce = true;
|
|
15969
16008
|
}
|
|
15970
16009
|
if (!this.callManagerStarted && !supersededByLeave()) {
|
|
@@ -16754,12 +16793,16 @@ class Call {
|
|
|
16754
16793
|
const streamStateProp = trackTypeToParticipantStreamKey(trackType);
|
|
16755
16794
|
if (!streamStateProp)
|
|
16756
16795
|
continue;
|
|
16757
|
-
this.state.updateParticipant(sessionId, (p) =>
|
|
16758
|
-
|
|
16759
|
-
|
|
16760
|
-
|
|
16761
|
-
|
|
16762
|
-
|
|
16796
|
+
this.state.updateParticipant(sessionId, (p) => {
|
|
16797
|
+
const currentStream = p[streamStateProp];
|
|
16798
|
+
const isEchoedStream = this.subscriber?.isSelfSubscribedStream(currentStream);
|
|
16799
|
+
return {
|
|
16800
|
+
publishedTracks: mediaStream
|
|
16801
|
+
? pushToIfMissing([...p.publishedTracks], trackType)
|
|
16802
|
+
: p.publishedTracks.filter((t) => t !== trackType),
|
|
16803
|
+
[streamStateProp]: isEchoedStream ? currentStream : mediaStream,
|
|
16804
|
+
};
|
|
16805
|
+
});
|
|
16763
16806
|
}
|
|
16764
16807
|
};
|
|
16765
16808
|
/**
|
|
@@ -17449,18 +17492,21 @@ class Call {
|
|
|
17449
17492
|
*
|
|
17450
17493
|
* @internal
|
|
17451
17494
|
*/
|
|
17452
|
-
applyDeviceConfig = async (settings,
|
|
17495
|
+
applyDeviceConfig = async (settings, opts) => {
|
|
17496
|
+
const { publish,
|
|
17497
|
+
// Skip speaker setup on RN until joining (explicitly passed as false during join())
|
|
17498
|
+
skipSpeakerApply = isReactNative(), withDisabledDevices, } = opts;
|
|
17453
17499
|
if (!skipSpeakerApply) {
|
|
17454
|
-
await this.speaker
|
|
17455
|
-
|
|
17456
|
-
|
|
17500
|
+
await this.speaker
|
|
17501
|
+
.apply(settings)
|
|
17502
|
+
.catch((err) => this.logger.warn('Speaker init failed', err));
|
|
17457
17503
|
}
|
|
17458
|
-
await this.camera
|
|
17459
|
-
|
|
17460
|
-
|
|
17461
|
-
await this.microphone
|
|
17462
|
-
|
|
17463
|
-
|
|
17504
|
+
await this.camera
|
|
17505
|
+
.apply(settings.video, publish, withDisabledDevices)
|
|
17506
|
+
.catch((err) => this.logger.warn('Camera init failed', err));
|
|
17507
|
+
await this.microphone
|
|
17508
|
+
.apply(settings.audio, publish, withDisabledDevices)
|
|
17509
|
+
.catch((err) => this.logger.warn('Mic init failed', err));
|
|
17464
17510
|
};
|
|
17465
17511
|
/**
|
|
17466
17512
|
* Will begin tracking the given element for visibility changes within the
|
|
@@ -18970,11 +19016,11 @@ class StreamClient {
|
|
|
18970
19016
|
return await this.wsConnection.connect(this.defaultWSTimeout);
|
|
18971
19017
|
};
|
|
18972
19018
|
getSdkVersion = () => this.options.clientAppIdentifier?.sdkVersion ||
|
|
18973
|
-
"1.
|
|
19019
|
+
"1.59.0";
|
|
18974
19020
|
getUserAgent = () => {
|
|
18975
19021
|
if (!this.cachedUserAgent) {
|
|
18976
19022
|
const { clientAppIdentifier = {} } = this.options;
|
|
18977
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
19023
|
+
const { sdkName = 'js', sdkVersion = "1.59.0", ...extras } = clientAppIdentifier;
|
|
18978
19024
|
this.cachedUserAgent = [
|
|
18979
19025
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
18980
19026
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -19337,7 +19383,6 @@ class ClientEventReporter {
|
|
|
19337
19383
|
user_id: this.streamClient.userID || this.coordinatorConnectUserId,
|
|
19338
19384
|
type: ctx?.callType,
|
|
19339
19385
|
id: ctx?.callId,
|
|
19340
|
-
call_cid: cid,
|
|
19341
19386
|
stage: 'JoinInitiated',
|
|
19342
19387
|
join_attempt_id: joinAttemptId,
|
|
19343
19388
|
...(coordinatorConnectId && {
|
|
@@ -19519,7 +19564,6 @@ class ClientEventReporter {
|
|
|
19519
19564
|
startedAt: Date.now(),
|
|
19520
19565
|
joinAttemptIdSnapshot: this.joinAttemptIds.get(cid),
|
|
19521
19566
|
sfuId: this.getSfuId(cid),
|
|
19522
|
-
userSessionId: this.getUserSessionId(cid),
|
|
19523
19567
|
wasPreviouslyConnected: this.pcEverConnected.get(key) === true,
|
|
19524
19568
|
lastIceState: iceConnectionState,
|
|
19525
19569
|
};
|
|
@@ -19530,9 +19574,6 @@ class ClientEventReporter {
|
|
|
19530
19574
|
peer_connection: role,
|
|
19531
19575
|
was_previously_connected: pair.wasPreviouslyConnected,
|
|
19532
19576
|
...(pair.sfuId && { sfu_id: pair.sfuId }),
|
|
19533
|
-
...(pair.userSessionId && {
|
|
19534
|
-
user_session_id: pair.userSessionId,
|
|
19535
|
-
}),
|
|
19536
19577
|
event_type: 'initiated',
|
|
19537
19578
|
});
|
|
19538
19579
|
};
|
|
@@ -19547,9 +19588,6 @@ class ClientEventReporter {
|
|
|
19547
19588
|
peer_connection: role,
|
|
19548
19589
|
was_previously_connected: pair.wasPreviouslyConnected,
|
|
19549
19590
|
...(pair.sfuId && { sfu_id: pair.sfuId }),
|
|
19550
|
-
...(pair.userSessionId && {
|
|
19551
|
-
user_session_id: pair.userSessionId,
|
|
19552
|
-
}),
|
|
19553
19591
|
event_type: 'completed',
|
|
19554
19592
|
outcome: 'success',
|
|
19555
19593
|
retry_count_attempt: 0,
|
|
@@ -19567,9 +19605,6 @@ class ClientEventReporter {
|
|
|
19567
19605
|
...this.sessionIdField(cid),
|
|
19568
19606
|
peer_connection: role,
|
|
19569
19607
|
was_previously_connected: pair.wasPreviouslyConnected,
|
|
19570
|
-
...(pair.userSessionId && {
|
|
19571
|
-
user_session_id: pair.userSessionId,
|
|
19572
|
-
}),
|
|
19573
19608
|
...(pair.sfuId && { sfu_id: pair.sfuId }),
|
|
19574
19609
|
event_type: 'completed',
|
|
19575
19610
|
outcome: 'failure',
|
|
@@ -19582,7 +19617,6 @@ class ClientEventReporter {
|
|
|
19582
19617
|
this.peerConnectionPairs.delete(key);
|
|
19583
19618
|
};
|
|
19584
19619
|
getSfuId = (cid) => this.callContexts.get(cid)?.getSfuId() ?? '';
|
|
19585
|
-
getUserSessionId = (cid) => this.callContexts.get(cid)?.getUserSessionId() ?? '';
|
|
19586
19620
|
sessionIdField = (cid) => {
|
|
19587
19621
|
const callSessionId = this.callContexts.get(cid)?.getCallSessionId() ?? '';
|
|
19588
19622
|
return callSessionId ? { call_session_id: callSessionId } : {};
|
|
@@ -19594,7 +19628,6 @@ class ClientEventReporter {
|
|
|
19594
19628
|
user_id: this.streamClient.userID || this.coordinatorConnectUserId,
|
|
19595
19629
|
type: ctx?.callType ?? '',
|
|
19596
19630
|
id: ctx?.callId ?? '',
|
|
19597
|
-
call_cid: cid,
|
|
19598
19631
|
stage,
|
|
19599
19632
|
stage_id: pair.sid,
|
|
19600
19633
|
...(pair.joinAttemptIdSnapshot && {
|
|
@@ -19845,6 +19878,7 @@ class StreamVideoClient {
|
|
|
19845
19878
|
}
|
|
19846
19879
|
else {
|
|
19847
19880
|
await call.updateFromRingingEvent(e);
|
|
19881
|
+
await call.get();
|
|
19848
19882
|
}
|
|
19849
19883
|
}
|
|
19850
19884
|
else {
|
|
@@ -19883,6 +19917,13 @@ class StreamVideoClient {
|
|
|
19883
19917
|
this.logger.error(`Failed to init call from event ${e.type}`, err);
|
|
19884
19918
|
}
|
|
19885
19919
|
};
|
|
19920
|
+
/**
|
|
19921
|
+
* Queries the API for calls matching the given filters.
|
|
19922
|
+
* @param data the query data.
|
|
19923
|
+
*/
|
|
19924
|
+
doQueryCalls = (data) => {
|
|
19925
|
+
return this.streamClient.post('/calls', data);
|
|
19926
|
+
};
|
|
19886
19927
|
/**
|
|
19887
19928
|
* Rewatches the given calls with retry logic.
|
|
19888
19929
|
* @param callsToReWatch array of call IDs to rewatch
|
|
@@ -19893,10 +19934,16 @@ class StreamVideoClient {
|
|
|
19893
19934
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
19894
19935
|
try {
|
|
19895
19936
|
this.logger.info(`Rewatching calls ${callsToReWatch.join(', ')} attempt ${attempt + 1}`);
|
|
19896
|
-
await this.
|
|
19937
|
+
const response = await this.doQueryCalls({
|
|
19897
19938
|
watch: true,
|
|
19898
19939
|
filter_conditions: { cid: { $in: callsToReWatch } },
|
|
19899
19940
|
});
|
|
19941
|
+
for (const c of response.calls) {
|
|
19942
|
+
const call = this.writeableStateStore.findCall(c.call.type, c.call.id);
|
|
19943
|
+
if (call) {
|
|
19944
|
+
call.updateFromCallStateResponse(c);
|
|
19945
|
+
}
|
|
19946
|
+
}
|
|
19900
19947
|
return;
|
|
19901
19948
|
}
|
|
19902
19949
|
catch (err) {
|
|
@@ -20039,9 +20086,11 @@ class StreamVideoClient {
|
|
|
20039
20086
|
* Will query the API for calls matching the given filters.
|
|
20040
20087
|
*
|
|
20041
20088
|
* @param data the query data.
|
|
20089
|
+
* @param opts additional options, for tweaking the API behavior.
|
|
20042
20090
|
*/
|
|
20043
|
-
queryCalls = async (data = {}) => {
|
|
20044
|
-
const
|
|
20091
|
+
queryCalls = async (data = {}, opts = {}) => {
|
|
20092
|
+
const { withDisabledDevices = true } = opts;
|
|
20093
|
+
const response = await this.doQueryCalls(data);
|
|
20045
20094
|
const calls = [];
|
|
20046
20095
|
for (const c of response.calls) {
|
|
20047
20096
|
const call = new Call({
|
|
@@ -20055,7 +20104,10 @@ class StreamVideoClient {
|
|
|
20055
20104
|
clientStore: this.writeableStateStore,
|
|
20056
20105
|
});
|
|
20057
20106
|
call.state.updateFromCallResponse(c.call);
|
|
20058
|
-
await call.applyDeviceConfig(c.call.settings,
|
|
20107
|
+
await call.applyDeviceConfig(c.call.settings, {
|
|
20108
|
+
publish: false,
|
|
20109
|
+
withDisabledDevices,
|
|
20110
|
+
});
|
|
20059
20111
|
if (data.watch) {
|
|
20060
20112
|
await call.setup();
|
|
20061
20113
|
this.writeableStateStore.registerCall(call);
|
|
@@ -20207,6 +20259,202 @@ const humanize = (n) => {
|
|
|
20207
20259
|
return String(n);
|
|
20208
20260
|
};
|
|
20209
20261
|
|
|
20262
|
+
/** Default loopback stream wait timeout. */
|
|
20263
|
+
const LOOPBACK_STREAMS_WAIT_TIMEOUT_MS = 10 * 1000;
|
|
20264
|
+
/** Default loopback recording duration. */
|
|
20265
|
+
const DEFAULT_LOOPBACK_RECORDING_DURATION_MS = 10 * 1000;
|
|
20266
|
+
/** Shortest permitted loopback recording duration. */
|
|
20267
|
+
const MIN_LOOPBACK_RECORDING_DURATION_MS = 5 * 1000;
|
|
20268
|
+
/** Longest permitted loopback recording duration. */
|
|
20269
|
+
const MAX_LOOPBACK_RECORDING_DURATION_MS = 2 * 60 * 1000;
|
|
20270
|
+
/** Thrown when loopback tracks do not arrive in time. */
|
|
20271
|
+
class LoopbackStreamsTimeoutError extends Error {
|
|
20272
|
+
constructor() {
|
|
20273
|
+
super('Timed out waiting for loopback streams. Ensure the call was joined ' +
|
|
20274
|
+
'with `allowOwnTracksLoopback: true` and that the SFU is configured ' +
|
|
20275
|
+
'to echo self-sub tracks.');
|
|
20276
|
+
this.name = 'LoopbackStreamsTimeoutError';
|
|
20277
|
+
}
|
|
20278
|
+
}
|
|
20279
|
+
/**
|
|
20280
|
+
* Returns the streams on the given participant that the SFU echoed back to us,
|
|
20281
|
+
* ignoring the local capture streams the publishing path writes to the same
|
|
20282
|
+
* fields.
|
|
20283
|
+
*/
|
|
20284
|
+
const getLoopbackStreams = (call, participant) => {
|
|
20285
|
+
const { subscriber } = call ?? {};
|
|
20286
|
+
const { audioStream, videoStream } = participant ?? {};
|
|
20287
|
+
return {
|
|
20288
|
+
loopbackVideoStream: subscriber?.isSelfSubscribedStream(videoStream)
|
|
20289
|
+
? videoStream
|
|
20290
|
+
: undefined,
|
|
20291
|
+
loopbackAudioStream: subscriber?.isSelfSubscribedStream(audioStream)
|
|
20292
|
+
? audioStream
|
|
20293
|
+
: undefined,
|
|
20294
|
+
};
|
|
20295
|
+
};
|
|
20296
|
+
/**
|
|
20297
|
+
* Returns loopback tracks when all required tracks are present.
|
|
20298
|
+
*
|
|
20299
|
+
* Prefer {@link waitForLoopbackStreams}, which observes call state until the
|
|
20300
|
+
* tracks arrive. This is the synchronous snapshot it is built from.
|
|
20301
|
+
*
|
|
20302
|
+
* @internal
|
|
20303
|
+
*/
|
|
20304
|
+
const getLoopbackTracks = (call, participant, includeVideo) => {
|
|
20305
|
+
if (!participant)
|
|
20306
|
+
return undefined;
|
|
20307
|
+
const { loopbackAudioStream, loopbackVideoStream } = getLoopbackStreams(call, participant);
|
|
20308
|
+
const audioTrack = loopbackAudioStream?.getAudioTracks()[0];
|
|
20309
|
+
const videoTrack = includeVideo
|
|
20310
|
+
? loopbackVideoStream?.getVideoTracks()[0]
|
|
20311
|
+
: undefined;
|
|
20312
|
+
if (!audioTrack || (includeVideo && !videoTrack)) {
|
|
20313
|
+
return undefined;
|
|
20314
|
+
}
|
|
20315
|
+
return { audioTrack, videoTrack };
|
|
20316
|
+
};
|
|
20317
|
+
/** Waits for the requested loopback tracks. */
|
|
20318
|
+
const waitForLoopbackStreams = (call, opts) => {
|
|
20319
|
+
const callState = call.state;
|
|
20320
|
+
const { includeVideo, signal, timeoutMs = LOOPBACK_STREAMS_WAIT_TIMEOUT_MS, } = opts;
|
|
20321
|
+
return new Promise((resolve, reject) => {
|
|
20322
|
+
const initial = getLoopbackTracks(call, callState.localParticipant, includeVideo);
|
|
20323
|
+
if (initial) {
|
|
20324
|
+
resolve(initial);
|
|
20325
|
+
return;
|
|
20326
|
+
}
|
|
20327
|
+
if (signal.aborted) {
|
|
20328
|
+
resolve(null);
|
|
20329
|
+
return;
|
|
20330
|
+
}
|
|
20331
|
+
let settled = false;
|
|
20332
|
+
const subscriptions = new Subscription();
|
|
20333
|
+
const cleanup = () => {
|
|
20334
|
+
if (settled)
|
|
20335
|
+
return;
|
|
20336
|
+
settled = true;
|
|
20337
|
+
clearTimeout(timeoutId);
|
|
20338
|
+
signal.removeEventListener('abort', onAbort);
|
|
20339
|
+
subscriptions.unsubscribe();
|
|
20340
|
+
};
|
|
20341
|
+
const onAbort = () => {
|
|
20342
|
+
cleanup();
|
|
20343
|
+
resolve(null);
|
|
20344
|
+
};
|
|
20345
|
+
const timeoutId = setTimeout(() => {
|
|
20346
|
+
cleanup();
|
|
20347
|
+
reject(new LoopbackStreamsTimeoutError());
|
|
20348
|
+
}, timeoutMs);
|
|
20349
|
+
signal.addEventListener('abort', onAbort);
|
|
20350
|
+
subscriptions.add(callState.localParticipant$.subscribe((participant) => {
|
|
20351
|
+
const ready = getLoopbackTracks(call, participant, includeVideo);
|
|
20352
|
+
if (ready) {
|
|
20353
|
+
cleanup();
|
|
20354
|
+
resolve(ready);
|
|
20355
|
+
}
|
|
20356
|
+
}));
|
|
20357
|
+
});
|
|
20358
|
+
};
|
|
20359
|
+
/** Enables loopback audio while `fn` runs, then restores it. */
|
|
20360
|
+
const withLoopbackAudioEnabled = async (tracks, fn) => {
|
|
20361
|
+
const restore = enableLoopbackAudio(tracks);
|
|
20362
|
+
try {
|
|
20363
|
+
return await fn();
|
|
20364
|
+
}
|
|
20365
|
+
finally {
|
|
20366
|
+
restore();
|
|
20367
|
+
}
|
|
20368
|
+
};
|
|
20369
|
+
/** Enables loopback audio and returns a restore function. */
|
|
20370
|
+
const enableLoopbackAudio = (tracks) => {
|
|
20371
|
+
const { audioTrack } = tracks;
|
|
20372
|
+
const previous = audioTrack?.enabled ?? false;
|
|
20373
|
+
if (audioTrack) {
|
|
20374
|
+
audioTrack.enabled = true;
|
|
20375
|
+
}
|
|
20376
|
+
return () => {
|
|
20377
|
+
if (audioTrack) {
|
|
20378
|
+
audioTrack.enabled = previous;
|
|
20379
|
+
}
|
|
20380
|
+
};
|
|
20381
|
+
};
|
|
20382
|
+
/** Clamps and rounds a loopback recording duration. */
|
|
20383
|
+
const clampLoopbackRecordingDuration = (durationMs) => Math.round(Math.min(MAX_LOOPBACK_RECORDING_DURATION_MS, Math.max(MIN_LOOPBACK_RECORDING_DURATION_MS, durationMs)));
|
|
20384
|
+
|
|
20385
|
+
/** Single-use `MediaRecorder` wrapper that returns an in-memory blob. */
|
|
20386
|
+
class MediaStreamRecorder {
|
|
20387
|
+
recording = promiseWithResolvers();
|
|
20388
|
+
logger = videoLoggerSystem.getLogger('MediaStreamRecorder');
|
|
20389
|
+
chunks = [];
|
|
20390
|
+
timeoutId;
|
|
20391
|
+
recorder;
|
|
20392
|
+
tracks;
|
|
20393
|
+
maxDurationMs;
|
|
20394
|
+
constructor(stream, options = {}) {
|
|
20395
|
+
if (typeof MediaRecorder === 'undefined') {
|
|
20396
|
+
throw new Error('MediaRecorder is not available in this environment');
|
|
20397
|
+
}
|
|
20398
|
+
this.tracks = stream.getTracks();
|
|
20399
|
+
if (this.tracks.length === 0) {
|
|
20400
|
+
throw new Error('cannot record a MediaStream that has no tracks');
|
|
20401
|
+
}
|
|
20402
|
+
this.maxDurationMs = options.maxDurationMs;
|
|
20403
|
+
this.recorder = new MediaRecorder(new MediaStream(this.tracks));
|
|
20404
|
+
this.recorder.ondataavailable = (event) => {
|
|
20405
|
+
if (event.data?.size)
|
|
20406
|
+
this.chunks.push(event.data);
|
|
20407
|
+
};
|
|
20408
|
+
this.recorder.onstop = this.handleStop;
|
|
20409
|
+
this.recorder.onerror = this.handleError;
|
|
20410
|
+
}
|
|
20411
|
+
/** Starts recording and resolves when recording stops. */
|
|
20412
|
+
start = () => {
|
|
20413
|
+
if (this.isSettled()) {
|
|
20414
|
+
throw new Error('MediaStreamRecorder is single-use: this instance has already ' +
|
|
20415
|
+
'recorded. Construct a new one.');
|
|
20416
|
+
}
|
|
20417
|
+
if (this.recorder.state === 'inactive') {
|
|
20418
|
+
this.tracks.forEach((track) => track.addEventListener('ended', this.stop));
|
|
20419
|
+
if (this.maxDurationMs !== undefined) {
|
|
20420
|
+
this.timeoutId = setTimeout(this.stop, this.maxDurationMs);
|
|
20421
|
+
}
|
|
20422
|
+
this.recorder.start();
|
|
20423
|
+
}
|
|
20424
|
+
return this.recording.promise;
|
|
20425
|
+
};
|
|
20426
|
+
/** Stops recording early. */
|
|
20427
|
+
stop = () => {
|
|
20428
|
+
if (this.recorder.state !== 'inactive')
|
|
20429
|
+
this.recorder.stop();
|
|
20430
|
+
};
|
|
20431
|
+
isSettled = () => this.recording.isResolved() || this.recording.isRejected();
|
|
20432
|
+
detach = () => {
|
|
20433
|
+
clearTimeout(this.timeoutId);
|
|
20434
|
+
this.tracks.forEach((track) => track.removeEventListener('ended', this.stop));
|
|
20435
|
+
};
|
|
20436
|
+
handleStop = () => {
|
|
20437
|
+
this.detach();
|
|
20438
|
+
if (this.chunks.length === 0) {
|
|
20439
|
+
this.logger.warn('MediaRecorder recording produced no data');
|
|
20440
|
+
this.recording.resolve(null);
|
|
20441
|
+
return;
|
|
20442
|
+
}
|
|
20443
|
+
const mimeType = this.chunks[0].type || this.recorder.mimeType;
|
|
20444
|
+
this.recording.resolve({
|
|
20445
|
+
blob: new Blob(this.chunks, { type: mimeType }),
|
|
20446
|
+
});
|
|
20447
|
+
};
|
|
20448
|
+
handleError = (event) => {
|
|
20449
|
+
if (this.isSettled())
|
|
20450
|
+
return;
|
|
20451
|
+
this.detach();
|
|
20452
|
+
const error = event.error;
|
|
20453
|
+
this.logger.error('MediaRecorder error', error);
|
|
20454
|
+
this.recording.reject(error ?? new Error('MediaRecorder failed'));
|
|
20455
|
+
};
|
|
20456
|
+
}
|
|
20457
|
+
|
|
20210
20458
|
/**
|
|
20211
20459
|
* Tiny, type-safe event emitter.
|
|
20212
20460
|
*
|
|
@@ -20525,5 +20773,5 @@ class EncryptionManager extends TypedEventEmitter {
|
|
|
20525
20773
|
};
|
|
20526
20774
|
}
|
|
20527
20775
|
|
|
20528
|
-
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, EncryptionManager, EncryptionSettingsRequestModeEnum, EncryptionSettingsResponseModeEnum, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, HLSSettingsRequestQualityTracksEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsRequestOutputTypesEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RawRecordingSettingsRequestModeEnum, RawRecordingSettingsResponseModeEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, RequestPermissionRequestPermissionsEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, SfuJoinError, models as SfuModels, SfuTimeoutError, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, TranslationSettingsLanguagesEnum, UpdateUserPermissionsRequestGrantPermissionsEnum, UpdateUserPermissionsRequestRevokePermissionsEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, WSAuthMessageProductsEnum, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getCallMediaEngineProvider, getClientDetails, getDeviceState, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio$1 as hasAudio, hasInterruptedTrack, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, humanize, isPinned, livestreamOrAudioRoomSortPreset, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setCallMediaEngineProvider, setDeviceInfo, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, videoLoggerSystem, withParticipantSource };
|
|
20776
|
+
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DEFAULT_LOOPBACK_RECORDING_DURATION_MS, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, EncryptionManager, EncryptionSettingsRequestModeEnum, EncryptionSettingsResponseModeEnum, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, HLSSettingsRequestQualityTracksEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsRequestOutputTypesEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LOOPBACK_STREAMS_WAIT_TIMEOUT_MS, LayoutSettingsRequestNameEnum, LoopbackStreamsTimeoutError, MAX_LOOPBACK_RECORDING_DURATION_MS, MIN_LOOPBACK_RECORDING_DURATION_MS, MediaStreamRecorder, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RawRecordingSettingsRequestModeEnum, RawRecordingSettingsResponseModeEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, RequestPermissionRequestPermissionsEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, SfuJoinError, models as SfuModels, SfuTimeoutError, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, TranslationSettingsLanguagesEnum, UpdateUserPermissionsRequestGrantPermissionsEnum, UpdateUserPermissionsRequestRevokePermissionsEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, WSAuthMessageProductsEnum, checkIfAudioOutputChangeSupported, clampLoopbackRecordingDuration, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, enableLoopbackAudio, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getCallMediaEngineProvider, getClientDetails, getDeviceState, getLoopbackStreams, getLoopbackTracks, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio$1 as hasAudio, hasInterruptedTrack, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, humanize, isPinned, livestreamOrAudioRoomSortPreset, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setCallMediaEngineProvider, setDeviceInfo, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, videoLoggerSystem, waitForLoopbackStreams, withLoopbackAudioEnabled, withParticipantSource };
|
|
20529
20777
|
//# sourceMappingURL=index.browser.es.js.map
|