@stream-io/video-client 0.6.7 → 0.6.9
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 +14 -0
- package/dist/index.browser.es.js +138 -17
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +138 -17
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +138 -17
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +23 -1
- package/dist/src/coordinator/connection/client.d.ts +6 -6
- package/dist/src/devices/InputMediaDeviceManager.d.ts +18 -1
- package/dist/src/devices/SpeakerManager.d.ts +6 -1
- package/dist/src/gen/coordinator/index.d.ts +434 -2
- package/dist/src/rtc/signal.d.ts +1 -0
- package/package.json +4 -4
- package/src/Call.ts +61 -4
- package/src/devices/InputMediaDeviceManager.ts +140 -48
- package/src/devices/SpeakerManager.ts +6 -1
- package/src/devices/__tests__/InputMediaDeviceManagerFilters.test.ts +115 -0
- package/src/gen/coordinator/index.ts +438 -2
- package/src/helpers/DynascaleManager.ts +0 -2
- package/src/store/CallState.ts +5 -0
package/dist/index.cjs.js
CHANGED
|
@@ -5825,7 +5825,7 @@ class SignalServerClient {
|
|
|
5825
5825
|
const defaultOptions = {
|
|
5826
5826
|
baseUrl: '',
|
|
5827
5827
|
sendJson: true,
|
|
5828
|
-
timeout: 5 * 1000,
|
|
5828
|
+
timeout: 5 * 1000, // ms.
|
|
5829
5829
|
jsonOptions: {
|
|
5830
5830
|
ignoreUnknownFields: true,
|
|
5831
5831
|
},
|
|
@@ -6231,7 +6231,7 @@ const findOptimalScreenSharingLayers = (videoTrack, preferences) => {
|
|
|
6231
6231
|
return [
|
|
6232
6232
|
{
|
|
6233
6233
|
active: true,
|
|
6234
|
-
rid: 'q',
|
|
6234
|
+
rid: 'q', // single track, start from 'q'
|
|
6235
6235
|
width: settings.width || 0,
|
|
6236
6236
|
height: settings.height || 0,
|
|
6237
6237
|
scaleResolutionDownBy: 1,
|
|
@@ -7084,9 +7084,14 @@ class CallState {
|
|
|
7084
7084
|
this.thumbnails$ = this.thumbnailsSubject.asObservable();
|
|
7085
7085
|
this.eventHandlers = {
|
|
7086
7086
|
// these events are not updating the call state:
|
|
7087
|
+
'call.closed_caption': undefined,
|
|
7087
7088
|
'call.permission_request': undefined,
|
|
7088
7089
|
'call.recording_failed': undefined,
|
|
7089
7090
|
'call.recording_ready': undefined,
|
|
7091
|
+
'call.transcription_started': undefined,
|
|
7092
|
+
'call.transcription_stopped': undefined,
|
|
7093
|
+
'call.transcription_ready': undefined,
|
|
7094
|
+
'call.transcription_failed': undefined,
|
|
7090
7095
|
'call.user_muted': undefined,
|
|
7091
7096
|
'connection.error': undefined,
|
|
7092
7097
|
'connection.ok': undefined,
|
|
@@ -9919,7 +9924,6 @@ class DynascaleManager {
|
|
|
9919
9924
|
// play audio through the system's default device
|
|
9920
9925
|
const { selectedDevice } = this.call.speaker.state;
|
|
9921
9926
|
if (selectedDevice && 'setSinkId' in audioElement) {
|
|
9922
|
-
// @ts-expect-error setSinkId is not yet in the lib
|
|
9923
9927
|
audioElement.setSinkId(selectedDevice);
|
|
9924
9928
|
}
|
|
9925
9929
|
}
|
|
@@ -9929,7 +9933,6 @@ class DynascaleManager {
|
|
|
9929
9933
|
? null
|
|
9930
9934
|
: this.call.speaker.state.selectedDevice$.subscribe((deviceId) => {
|
|
9931
9935
|
if (deviceId) {
|
|
9932
|
-
// @ts-expect-error setSinkId is not yet in the lib
|
|
9933
9936
|
audioElement.setSinkId(deviceId);
|
|
9934
9937
|
}
|
|
9935
9938
|
});
|
|
@@ -10329,8 +10332,14 @@ class InputMediaDeviceManager {
|
|
|
10329
10332
|
this.stopOnLeave = true;
|
|
10330
10333
|
this.subscriptions = [];
|
|
10331
10334
|
this.isTrackStoppedDueToTrackEnd = false;
|
|
10332
|
-
this.
|
|
10333
|
-
|
|
10335
|
+
this.filters = [];
|
|
10336
|
+
/**
|
|
10337
|
+
* Disposes the manager.
|
|
10338
|
+
*
|
|
10339
|
+
* @internal
|
|
10340
|
+
*/
|
|
10341
|
+
this.dispose = () => {
|
|
10342
|
+
this.subscriptions.forEach((s) => s());
|
|
10334
10343
|
};
|
|
10335
10344
|
this.logger = getLogger([`${TrackType[trackType].toLowerCase()} manager`]);
|
|
10336
10345
|
if (deviceIds$ &&
|
|
@@ -10407,6 +10416,23 @@ class InputMediaDeviceManager {
|
|
|
10407
10416
|
return this.enable();
|
|
10408
10417
|
}
|
|
10409
10418
|
}
|
|
10419
|
+
/**
|
|
10420
|
+
* Registers a filter that will be applied to the stream.
|
|
10421
|
+
*
|
|
10422
|
+
* The registered filter will get the existing stream, and it should return
|
|
10423
|
+
* a new stream with the applied filter.
|
|
10424
|
+
*
|
|
10425
|
+
* @param filter the filter to register.
|
|
10426
|
+
* @returns a function that will unregister the filter.
|
|
10427
|
+
*/
|
|
10428
|
+
async registerFilter(filter) {
|
|
10429
|
+
this.filters.push(filter);
|
|
10430
|
+
await this.applySettingsToStream();
|
|
10431
|
+
return async () => {
|
|
10432
|
+
this.filters = this.filters.filter((f) => f !== filter);
|
|
10433
|
+
await this.applySettingsToStream();
|
|
10434
|
+
};
|
|
10435
|
+
}
|
|
10410
10436
|
/**
|
|
10411
10437
|
* Will set the default constraints for the device.
|
|
10412
10438
|
*
|
|
@@ -10502,7 +10528,62 @@ class InputMediaDeviceManager {
|
|
|
10502
10528
|
...defaultConstraints,
|
|
10503
10529
|
deviceId: this.state.selectedDevice,
|
|
10504
10530
|
};
|
|
10505
|
-
|
|
10531
|
+
/**
|
|
10532
|
+
* Chains two media streams together.
|
|
10533
|
+
*
|
|
10534
|
+
* In our case, filters MediaStreams are derived from their parent MediaStream.
|
|
10535
|
+
* However, once a child filter's track is stopped,
|
|
10536
|
+
* the tracks of the parent MediaStream aren't automatically stopped.
|
|
10537
|
+
* This leads to a situation where the camera indicator light is still on
|
|
10538
|
+
* even though the user stopped publishing video.
|
|
10539
|
+
*
|
|
10540
|
+
* This function works around this issue by stopping the parent MediaStream's tracks
|
|
10541
|
+
* as well once the child filter's tracks are stopped.
|
|
10542
|
+
*
|
|
10543
|
+
* It works by patching the stop() method of the child filter's tracks to also stop
|
|
10544
|
+
* the parent MediaStream's tracks of the same type. Here we assume that
|
|
10545
|
+
* the parent MediaStream has only one track of each type.
|
|
10546
|
+
*
|
|
10547
|
+
* @param parentStream the parent MediaStream. Omit for the root stream.
|
|
10548
|
+
*/
|
|
10549
|
+
const chainWith = (parentStream) => async (filterStream) => {
|
|
10550
|
+
if (!parentStream)
|
|
10551
|
+
return filterStream;
|
|
10552
|
+
// TODO OL: take care of track.enabled property as well
|
|
10553
|
+
const parent = await parentStream;
|
|
10554
|
+
filterStream.getTracks().forEach((track) => {
|
|
10555
|
+
const originalStop = track.stop;
|
|
10556
|
+
track.stop = function stop() {
|
|
10557
|
+
originalStop.call(track);
|
|
10558
|
+
parent.getTracks().forEach((parentTrack) => {
|
|
10559
|
+
if (parentTrack.kind === track.kind) {
|
|
10560
|
+
parentTrack.stop();
|
|
10561
|
+
}
|
|
10562
|
+
});
|
|
10563
|
+
};
|
|
10564
|
+
});
|
|
10565
|
+
parent.getTracks().forEach((parentTrack) => {
|
|
10566
|
+
// When the parent stream abruptly ends, we propagate the event
|
|
10567
|
+
// to the filter stream.
|
|
10568
|
+
// This usually happens when the camera/microphone permissions
|
|
10569
|
+
// are revoked or when the device is disconnected.
|
|
10570
|
+
const handleParentTrackEnded = () => {
|
|
10571
|
+
filterStream.getTracks().forEach((track) => {
|
|
10572
|
+
if (parentTrack.kind !== track.kind)
|
|
10573
|
+
return;
|
|
10574
|
+
track.stop();
|
|
10575
|
+
track.dispatchEvent(new Event('ended')); // propagate the event
|
|
10576
|
+
});
|
|
10577
|
+
};
|
|
10578
|
+
parentTrack.addEventListener('ended', handleParentTrackEnded);
|
|
10579
|
+
this.subscriptions.push(() => {
|
|
10580
|
+
parentTrack.removeEventListener('ended', handleParentTrackEnded);
|
|
10581
|
+
});
|
|
10582
|
+
});
|
|
10583
|
+
return filterStream;
|
|
10584
|
+
};
|
|
10585
|
+
// we publish the last MediaStream of the chain
|
|
10586
|
+
stream = await this.filters.reduce((parent, filter) => parent.then(filter).then(chainWith(parent)), this.getStream(constraints));
|
|
10506
10587
|
}
|
|
10507
10588
|
if (this.call.state.callingState === exports.CallingState.JOINED) {
|
|
10508
10589
|
await this.publishStream(stream);
|
|
@@ -10538,10 +10619,10 @@ class InputMediaDeviceManager {
|
|
|
10538
10619
|
return '';
|
|
10539
10620
|
}
|
|
10540
10621
|
handleDisconnectedOrReplacedDevices() {
|
|
10541
|
-
this.subscriptions.push(rxjs.combineLatest([
|
|
10622
|
+
this.subscriptions.push(createSubscription(rxjs.combineLatest([
|
|
10542
10623
|
deviceIds$.pipe(rxjs.pairwise()),
|
|
10543
10624
|
this.state.selectedDevice$,
|
|
10544
|
-
])
|
|
10625
|
+
]), async ([[prevDevices, currentDevices], deviceId]) => {
|
|
10545
10626
|
if (!deviceId) {
|
|
10546
10627
|
return;
|
|
10547
10628
|
}
|
|
@@ -11305,7 +11386,12 @@ class SpeakerManager {
|
|
|
11305
11386
|
constructor(call) {
|
|
11306
11387
|
this.state = new SpeakerState();
|
|
11307
11388
|
this.subscriptions = [];
|
|
11308
|
-
|
|
11389
|
+
/**
|
|
11390
|
+
* Disposes the manager.
|
|
11391
|
+
*
|
|
11392
|
+
* @internal
|
|
11393
|
+
*/
|
|
11394
|
+
this.dispose = () => {
|
|
11309
11395
|
this.subscriptions.forEach((s) => s.unsubscribe());
|
|
11310
11396
|
};
|
|
11311
11397
|
this.call = call;
|
|
@@ -11500,10 +11586,10 @@ class Call {
|
|
|
11500
11586
|
// Call all leave call hooks, e.g. to clean up global event handlers
|
|
11501
11587
|
this.leaveCallHooks.forEach((hook) => hook());
|
|
11502
11588
|
this.clientStore.unregisterCall(this);
|
|
11503
|
-
this.camera.
|
|
11504
|
-
this.microphone.
|
|
11505
|
-
this.screenShare.
|
|
11506
|
-
this.speaker.
|
|
11589
|
+
this.camera.dispose();
|
|
11590
|
+
this.microphone.dispose();
|
|
11591
|
+
this.screenShare.dispose();
|
|
11592
|
+
this.speaker.dispose();
|
|
11507
11593
|
const stopOnLeavePromises = [];
|
|
11508
11594
|
if (this.camera.stopOnLeave) {
|
|
11509
11595
|
stopOnLeavePromises.push(this.camera.disable(true));
|
|
@@ -12305,6 +12391,20 @@ class Call {
|
|
|
12305
12391
|
this.stopRecording = async () => {
|
|
12306
12392
|
return this.streamClient.post(`${this.streamClientBasePath}/stop_recording`, {});
|
|
12307
12393
|
};
|
|
12394
|
+
/**
|
|
12395
|
+
* Starts the transcription of the call.
|
|
12396
|
+
*
|
|
12397
|
+
* @param request the request data.
|
|
12398
|
+
*/
|
|
12399
|
+
this.startTranscription = async (request) => {
|
|
12400
|
+
return this.streamClient.post(`${this.streamClientBasePath}/start_transcription`, request);
|
|
12401
|
+
};
|
|
12402
|
+
/**
|
|
12403
|
+
* Stops the transcription of the call.
|
|
12404
|
+
*/
|
|
12405
|
+
this.stopTranscription = async () => {
|
|
12406
|
+
return this.streamClient.post(`${this.streamClientBasePath}/stop_transcription`);
|
|
12407
|
+
};
|
|
12308
12408
|
/**
|
|
12309
12409
|
* Sends a `call.permission_request` event to all users connected to the call. The call settings object contains infomration about which permissions can be requested during a call (for example a user might be allowed to request permission to publish audio, but not video).
|
|
12310
12410
|
*/
|
|
@@ -12513,6 +12613,14 @@ class Call {
|
|
|
12513
12613
|
}
|
|
12514
12614
|
return this.streamClient.get(`${endpoint}/recordings`);
|
|
12515
12615
|
};
|
|
12616
|
+
/**
|
|
12617
|
+
* Retrieves the list of transcriptions for the current call.
|
|
12618
|
+
*
|
|
12619
|
+
* @returns the list of transcriptions.
|
|
12620
|
+
*/
|
|
12621
|
+
this.queryTranscriptions = async () => {
|
|
12622
|
+
return this.streamClient.get(`${this.streamClientBasePath}/transcriptions`);
|
|
12623
|
+
};
|
|
12516
12624
|
/**
|
|
12517
12625
|
* Retrieve call statistics for a particular call session (historical).
|
|
12518
12626
|
* Here `callSessionID` is mandatory.
|
|
@@ -12524,6 +12632,19 @@ class Call {
|
|
|
12524
12632
|
const endpoint = `${this.streamClientBasePath}/stats/${callSessionID}`;
|
|
12525
12633
|
return this.streamClient.get(endpoint);
|
|
12526
12634
|
};
|
|
12635
|
+
/**
|
|
12636
|
+
* Submit feedback on call experience
|
|
12637
|
+
*
|
|
12638
|
+
* @returns
|
|
12639
|
+
*/
|
|
12640
|
+
this.submitFeedback = async (feedback) => {
|
|
12641
|
+
const callSessionId = this.state.session?.id;
|
|
12642
|
+
if (!callSessionId) {
|
|
12643
|
+
return;
|
|
12644
|
+
}
|
|
12645
|
+
const endpoint = `${this.streamClientBasePath}/feedback/${callSessionId}`;
|
|
12646
|
+
return this.streamClient.post(endpoint, feedback);
|
|
12647
|
+
};
|
|
12527
12648
|
/**
|
|
12528
12649
|
* Sends a custom event to all call participants.
|
|
12529
12650
|
*
|
|
@@ -13581,7 +13702,7 @@ function UserFromToken(token) {
|
|
|
13581
13702
|
*/
|
|
13582
13703
|
function DevToken(userId) {
|
|
13583
13704
|
return [
|
|
13584
|
-
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
|
|
13705
|
+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', //{"alg": "HS256", "typ": "JWT"}
|
|
13585
13706
|
encodeBase64(JSON.stringify({ user_id: userId })),
|
|
13586
13707
|
'devtoken', // hardcoded signature
|
|
13587
13708
|
].join('.');
|
|
@@ -14393,7 +14514,7 @@ class StreamClient {
|
|
|
14393
14514
|
});
|
|
14394
14515
|
};
|
|
14395
14516
|
this.getUserAgent = () => {
|
|
14396
|
-
const version = "0.6.
|
|
14517
|
+
const version = "0.6.9" ;
|
|
14397
14518
|
return (this.userAgent ||
|
|
14398
14519
|
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
|
|
14399
14520
|
};
|
|
@@ -14508,7 +14629,7 @@ class StreamClient {
|
|
|
14508
14629
|
}
|
|
14509
14630
|
this.options = {
|
|
14510
14631
|
timeout: 5000,
|
|
14511
|
-
withCredentials: false,
|
|
14632
|
+
withCredentials: false, // making sure cookies are not sent
|
|
14512
14633
|
warmUp: false,
|
|
14513
14634
|
...inputOptions,
|
|
14514
14635
|
};
|