@stream-io/video-client 1.13.1 → 1.15.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 +14 -0
- package/dist/index.browser.es.js +1704 -1762
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +1706 -1780
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +1704 -1762
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +61 -30
- package/dist/src/StreamSfuClient.d.ts +4 -5
- package/dist/src/devices/CameraManager.d.ts +5 -8
- package/dist/src/devices/InputMediaDeviceManager.d.ts +5 -5
- package/dist/src/devices/MicrophoneManager.d.ts +7 -2
- package/dist/src/devices/ScreenShareManager.d.ts +1 -2
- package/dist/src/gen/coordinator/index.d.ts +904 -515
- package/dist/src/gen/video/sfu/event/events.d.ts +38 -19
- package/dist/src/gen/video/sfu/models/models.d.ts +76 -9
- package/dist/src/helpers/array.d.ts +7 -0
- package/dist/src/permissions/PermissionsContext.d.ts +6 -0
- package/dist/src/rtc/BasePeerConnection.d.ts +90 -0
- package/dist/src/rtc/Dispatcher.d.ts +0 -1
- package/dist/src/rtc/IceTrickleBuffer.d.ts +3 -2
- package/dist/src/rtc/Publisher.d.ts +32 -86
- package/dist/src/rtc/Subscriber.d.ts +4 -56
- package/dist/src/rtc/TransceiverCache.d.ts +55 -0
- package/dist/src/rtc/codecs.d.ts +1 -15
- package/dist/src/rtc/helpers/sdp.d.ts +8 -0
- package/dist/src/rtc/helpers/tracks.d.ts +1 -0
- package/dist/src/rtc/index.d.ts +3 -0
- package/dist/src/rtc/videoLayers.d.ts +11 -25
- package/dist/src/stats/{stateStoreStatsReporter.d.ts → CallStateStatsReporter.d.ts} +5 -1
- package/dist/src/stats/SfuStatsReporter.d.ts +4 -2
- package/dist/src/stats/index.d.ts +1 -1
- package/dist/src/stats/types.d.ts +8 -0
- package/dist/src/store/CallState.d.ts +47 -5
- package/dist/src/store/rxUtils.d.ts +15 -1
- package/dist/src/types.d.ts +26 -22
- package/package.json +1 -1
- package/src/Call.ts +310 -271
- package/src/StreamSfuClient.ts +9 -14
- package/src/StreamVideoClient.ts +1 -1
- package/src/__tests__/Call.publishing.test.ts +306 -0
- package/src/devices/CameraManager.ts +33 -16
- package/src/devices/InputMediaDeviceManager.ts +36 -27
- package/src/devices/MicrophoneManager.ts +29 -8
- package/src/devices/ScreenShareManager.ts +6 -8
- package/src/devices/__tests__/CameraManager.test.ts +111 -14
- package/src/devices/__tests__/InputMediaDeviceManager.test.ts +4 -4
- package/src/devices/__tests__/MicrophoneManager.test.ts +59 -21
- package/src/devices/__tests__/ScreenShareManager.test.ts +5 -5
- package/src/devices/__tests__/mocks.ts +1 -0
- package/src/events/__tests__/internal.test.ts +132 -0
- package/src/events/__tests__/mutes.test.ts +0 -3
- package/src/events/__tests__/speaker.test.ts +92 -0
- package/src/events/participant.ts +3 -4
- package/src/gen/coordinator/index.ts +902 -514
- package/src/gen/video/sfu/event/events.ts +91 -30
- package/src/gen/video/sfu/models/models.ts +105 -13
- package/src/helpers/array.ts +14 -0
- package/src/permissions/PermissionsContext.ts +22 -0
- package/src/permissions/__tests__/PermissionsContext.test.ts +40 -0
- package/src/rpc/__tests__/createClient.test.ts +38 -0
- package/src/rpc/createClient.ts +11 -5
- package/src/rtc/BasePeerConnection.ts +240 -0
- package/src/rtc/Dispatcher.ts +0 -9
- package/src/rtc/IceTrickleBuffer.ts +24 -4
- package/src/rtc/Publisher.ts +210 -528
- package/src/rtc/Subscriber.ts +26 -200
- package/src/rtc/TransceiverCache.ts +120 -0
- package/src/rtc/__tests__/Publisher.test.ts +407 -210
- package/src/rtc/__tests__/Subscriber.test.ts +88 -36
- package/src/rtc/__tests__/mocks/webrtc.mocks.ts +22 -2
- package/src/rtc/__tests__/videoLayers.test.ts +161 -54
- package/src/rtc/codecs.ts +1 -131
- package/src/rtc/helpers/__tests__/rtcConfiguration.test.ts +34 -0
- package/src/rtc/helpers/__tests__/sdp.test.ts +59 -0
- package/src/rtc/helpers/sdp.ts +30 -0
- package/src/rtc/helpers/tracks.ts +3 -0
- package/src/rtc/index.ts +4 -0
- package/src/rtc/videoLayers.ts +68 -76
- package/src/stats/{stateStoreStatsReporter.ts → CallStateStatsReporter.ts} +58 -27
- package/src/stats/SfuStatsReporter.ts +31 -3
- package/src/stats/index.ts +1 -1
- package/src/stats/types.ts +12 -0
- package/src/store/CallState.ts +115 -5
- package/src/store/__tests__/CallState.test.ts +101 -0
- package/src/store/rxUtils.ts +23 -1
- package/src/types.ts +27 -22
- package/dist/src/helpers/sdp-munging.d.ts +0 -24
- package/dist/src/rtc/bitrateLookup.d.ts +0 -2
- package/dist/src/rtc/helpers/iceCandidate.d.ts +0 -2
- package/src/helpers/__tests__/hq-audio-sdp.ts +0 -332
- package/src/helpers/__tests__/sdp-munging.test.ts +0 -283
- package/src/helpers/sdp-munging.ts +0 -265
- package/src/rtc/__tests__/bitrateLookup.test.ts +0 -12
- package/src/rtc/__tests__/codecs.test.ts +0 -145
- package/src/rtc/bitrateLookup.ts +0 -61
- package/src/rtc/helpers/iceCandidate.ts +0 -16
- /package/dist/src/{compatibility.d.ts → helpers/compatibility.d.ts} +0 -0
- /package/src/{compatibility.ts → helpers/compatibility.ts} +0 -0
package/src/store/CallState.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { Patch } from './rxUtils';
|
|
|
9
9
|
import * as RxUtils from './rxUtils';
|
|
10
10
|
import { CallingState } from './CallingState';
|
|
11
11
|
import {
|
|
12
|
+
type ClosedCaptionsSettings,
|
|
12
13
|
type StreamVideoParticipant,
|
|
13
14
|
type StreamVideoParticipantPatch,
|
|
14
15
|
type StreamVideoParticipantPatches,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
import { CallStatsReport } from '../stats';
|
|
20
21
|
import {
|
|
21
22
|
BlockedUserEvent,
|
|
23
|
+
CallClosedCaption,
|
|
22
24
|
CallHLSBroadcastingStartedEvent,
|
|
23
25
|
CallIngressResponse,
|
|
24
26
|
CallMemberAddedEvent,
|
|
@@ -32,6 +34,7 @@ import {
|
|
|
32
34
|
CallSessionParticipantLeftEvent,
|
|
33
35
|
CallSessionResponse,
|
|
34
36
|
CallSettingsResponse,
|
|
37
|
+
ClosedCaptionEvent,
|
|
35
38
|
EgressResponse,
|
|
36
39
|
MemberResponse,
|
|
37
40
|
OwnCapability,
|
|
@@ -97,6 +100,7 @@ export class CallState {
|
|
|
97
100
|
CallSettingsResponse | undefined
|
|
98
101
|
>(undefined);
|
|
99
102
|
private transcribingSubject = new BehaviorSubject<boolean>(false);
|
|
103
|
+
private captioningSubject = new BehaviorSubject<boolean>(false);
|
|
100
104
|
private endedBySubject = new BehaviorSubject<UserResponse | undefined>(
|
|
101
105
|
undefined,
|
|
102
106
|
);
|
|
@@ -117,6 +121,7 @@ export class CallState {
|
|
|
117
121
|
private callStatsReportSubject = new BehaviorSubject<
|
|
118
122
|
CallStatsReport | undefined
|
|
119
123
|
>(undefined);
|
|
124
|
+
private closedCaptionsSubject = new BehaviorSubject<CallClosedCaption[]>([]);
|
|
120
125
|
|
|
121
126
|
// These are tracks that were delivered to the Subscriber's onTrack event
|
|
122
127
|
// that we couldn't associate with a participant yet.
|
|
@@ -275,6 +280,11 @@ export class CallState {
|
|
|
275
280
|
*/
|
|
276
281
|
transcribing$: Observable<boolean>;
|
|
277
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Will provide the closed captioning state of this call.
|
|
285
|
+
*/
|
|
286
|
+
captioning$: Observable<boolean>;
|
|
287
|
+
|
|
278
288
|
/**
|
|
279
289
|
* Will provide the user who ended this call.
|
|
280
290
|
*/
|
|
@@ -285,15 +295,24 @@ export class CallState {
|
|
|
285
295
|
*/
|
|
286
296
|
thumbnails$: Observable<ThumbnailResponse | undefined>;
|
|
287
297
|
|
|
298
|
+
/**
|
|
299
|
+
* The queue of closed captions.
|
|
300
|
+
*/
|
|
301
|
+
closedCaptions$: Observable<CallClosedCaption[]>;
|
|
302
|
+
|
|
288
303
|
readonly logger = getLogger(['CallState']);
|
|
289
304
|
|
|
290
305
|
/**
|
|
291
306
|
* A list of comparators that are used to sort the participants.
|
|
292
|
-
*
|
|
293
|
-
* @private
|
|
294
307
|
*/
|
|
295
308
|
private sortParticipantsBy = defaultSortPreset;
|
|
296
309
|
|
|
310
|
+
/**
|
|
311
|
+
* The closed captions configuration.
|
|
312
|
+
*/
|
|
313
|
+
private closedCaptionsSettings: ClosedCaptionsSettings | undefined;
|
|
314
|
+
private closedCaptionsTasks = new Map<string, NodeJS.Timeout>();
|
|
315
|
+
|
|
297
316
|
private readonly eventHandlers: {
|
|
298
317
|
[EventType in WSEvent['type']]:
|
|
299
318
|
| ((event: Extract<WSEvent, { type: EventType }>) => void)
|
|
@@ -357,6 +376,7 @@ export class CallState {
|
|
|
357
376
|
this.settings$ = this.settingsSubject.asObservable();
|
|
358
377
|
this.endedBy$ = this.endedBySubject.asObservable();
|
|
359
378
|
this.thumbnails$ = this.thumbnailsSubject.asObservable();
|
|
379
|
+
this.closedCaptions$ = this.closedCaptionsSubject.asObservable();
|
|
360
380
|
|
|
361
381
|
/**
|
|
362
382
|
* Performs shallow comparison of two arrays.
|
|
@@ -390,10 +410,10 @@ export class CallState {
|
|
|
390
410
|
this.participantCount$ = duc(this.participantCountSubject);
|
|
391
411
|
this.recording$ = duc(this.recordingSubject);
|
|
392
412
|
this.transcribing$ = duc(this.transcribingSubject);
|
|
413
|
+
this.captioning$ = duc(this.captioningSubject);
|
|
393
414
|
|
|
394
415
|
this.eventHandlers = {
|
|
395
416
|
// these events are not updating the call state:
|
|
396
|
-
'call.closed_caption': undefined,
|
|
397
417
|
'call.deleted': undefined,
|
|
398
418
|
'call.permission_request': undefined,
|
|
399
419
|
'call.recording_ready': undefined,
|
|
@@ -415,6 +435,16 @@ export class CallState {
|
|
|
415
435
|
// events that update call state:
|
|
416
436
|
'call.accepted': (e) => this.updateFromCallResponse(e.call),
|
|
417
437
|
'call.blocked_user': this.blockUser,
|
|
438
|
+
'call.closed_caption': this.updateFromClosedCaptions,
|
|
439
|
+
'call.closed_captions_failed': () => {
|
|
440
|
+
this.setCurrentValue(this.captioningSubject, false);
|
|
441
|
+
},
|
|
442
|
+
'call.closed_captions_started': () => {
|
|
443
|
+
this.setCurrentValue(this.captioningSubject, true);
|
|
444
|
+
},
|
|
445
|
+
'call.closed_captions_stopped': () => {
|
|
446
|
+
this.setCurrentValue(this.captioningSubject, false);
|
|
447
|
+
},
|
|
418
448
|
'call.created': (e) => this.updateFromCallResponse(e.call),
|
|
419
449
|
'call.ended': (e) => {
|
|
420
450
|
this.updateFromCallResponse(e.call);
|
|
@@ -464,6 +494,16 @@ export class CallState {
|
|
|
464
494
|
};
|
|
465
495
|
}
|
|
466
496
|
|
|
497
|
+
/**
|
|
498
|
+
* Runs the cleanup tasks.
|
|
499
|
+
*/
|
|
500
|
+
dispose = () => {
|
|
501
|
+
for (const [ccKey, taskId] of this.closedCaptionsTasks.entries()) {
|
|
502
|
+
clearTimeout(taskId);
|
|
503
|
+
this.closedCaptionsTasks.delete(ccKey);
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
467
507
|
/**
|
|
468
508
|
* Sets the list of criteria that are used to sort the participants.
|
|
469
509
|
* To disable sorting, you can pass `noopComparator()`.
|
|
@@ -533,6 +573,23 @@ export class CallState {
|
|
|
533
573
|
return this.setCurrentValue(this.startedAtSubject, startedAt);
|
|
534
574
|
};
|
|
535
575
|
|
|
576
|
+
/**
|
|
577
|
+
* Returns whether closed captions are enabled in the current call.
|
|
578
|
+
*/
|
|
579
|
+
get captioning() {
|
|
580
|
+
return this.getCurrentValue(this.captioning$);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Sets the closed captioning state of the current call.
|
|
585
|
+
*
|
|
586
|
+
* @internal
|
|
587
|
+
* @param captioning the closed captioning state.
|
|
588
|
+
*/
|
|
589
|
+
setCaptioning = (captioning: boolean) => {
|
|
590
|
+
return RxUtils.updateValue(this.captioningSubject, captioning);
|
|
591
|
+
};
|
|
592
|
+
|
|
536
593
|
/**
|
|
537
594
|
* The server-side counted number of anonymous participants connected to the current call.
|
|
538
595
|
* This number includes the anonymous participants as well.
|
|
@@ -792,6 +849,13 @@ export class CallState {
|
|
|
792
849
|
return this.getCurrentValue(this.thumbnails$);
|
|
793
850
|
}
|
|
794
851
|
|
|
852
|
+
/**
|
|
853
|
+
* Returns the current queue of closed captions.
|
|
854
|
+
*/
|
|
855
|
+
get closedCaptions() {
|
|
856
|
+
return this.getCurrentValue(this.closedCaptions$);
|
|
857
|
+
}
|
|
858
|
+
|
|
795
859
|
/**
|
|
796
860
|
* Will try to find the participant with the given sessionId in the current call.
|
|
797
861
|
*
|
|
@@ -840,7 +904,6 @@ export class CallState {
|
|
|
840
904
|
|
|
841
905
|
const thePatch = typeof patch === 'function' ? patch(participant) : patch;
|
|
842
906
|
const updatedParticipant: StreamVideoParticipant = {
|
|
843
|
-
// FIXME OL: this is not a deep merge, we might want to revisit this
|
|
844
907
|
...participant,
|
|
845
908
|
...thePatch,
|
|
846
909
|
};
|
|
@@ -912,7 +975,6 @@ export class CallState {
|
|
|
912
975
|
*
|
|
913
976
|
* @param trackType the kind of subscription to update.
|
|
914
977
|
* @param changes the list of subscription changes to do.
|
|
915
|
-
* @param type the debounce type to use for the update.
|
|
916
978
|
*/
|
|
917
979
|
updateParticipantTracks = (
|
|
918
980
|
trackType: VideoTrackType,
|
|
@@ -1040,6 +1102,15 @@ export class CallState {
|
|
|
1040
1102
|
return orphans;
|
|
1041
1103
|
};
|
|
1042
1104
|
|
|
1105
|
+
/**
|
|
1106
|
+
* Updates the closed captions settings.
|
|
1107
|
+
*
|
|
1108
|
+
* @param config the new closed captions settings.
|
|
1109
|
+
*/
|
|
1110
|
+
updateClosedCaptionSettings = (config: Partial<ClosedCaptionsSettings>) => {
|
|
1111
|
+
this.closedCaptionsSettings = { ...this.closedCaptionsSettings, ...config };
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1043
1114
|
/**
|
|
1044
1115
|
* Updates the call state with the data received from the server.
|
|
1045
1116
|
*
|
|
@@ -1066,6 +1137,7 @@ export class CallState {
|
|
|
1066
1137
|
this.updateParticipantCountFromSession(s);
|
|
1067
1138
|
this.setCurrentValue(this.settingsSubject, call.settings);
|
|
1068
1139
|
this.setCurrentValue(this.transcribingSubject, call.transcribing);
|
|
1140
|
+
this.setCurrentValue(this.captioningSubject, call.captioning);
|
|
1069
1141
|
this.setCurrentValue(this.thumbnailsSubject, call.thumbnails);
|
|
1070
1142
|
};
|
|
1071
1143
|
|
|
@@ -1299,4 +1371,42 @@ export class CallState {
|
|
|
1299
1371
|
this.setCurrentValue(this.ownCapabilitiesSubject, event.own_capabilities);
|
|
1300
1372
|
}
|
|
1301
1373
|
};
|
|
1374
|
+
|
|
1375
|
+
private updateFromClosedCaptions = (event: ClosedCaptionEvent) => {
|
|
1376
|
+
this.setCurrentValue(this.closedCaptionsSubject, (queue) => {
|
|
1377
|
+
const { closed_caption } = event;
|
|
1378
|
+
|
|
1379
|
+
const keyOf = (c: CallClosedCaption) => `${c.speaker_id}/${c.start_time}`;
|
|
1380
|
+
const currentKey = keyOf(closed_caption);
|
|
1381
|
+
|
|
1382
|
+
const duplicate = queue.some((caption) => keyOf(caption) === currentKey);
|
|
1383
|
+
if (duplicate) return queue;
|
|
1384
|
+
|
|
1385
|
+
const nextQueue = [...queue, closed_caption];
|
|
1386
|
+
|
|
1387
|
+
const { visibilityDurationMs = 2700, maxVisibleCaptions = 2 } =
|
|
1388
|
+
this.closedCaptionsSettings || {};
|
|
1389
|
+
// schedule the removal of the closed caption after the retention time
|
|
1390
|
+
if (visibilityDurationMs > 0) {
|
|
1391
|
+
const taskId = setTimeout(() => {
|
|
1392
|
+
this.setCurrentValue(this.closedCaptionsSubject, (captions) =>
|
|
1393
|
+
captions.filter((caption) => caption !== closed_caption),
|
|
1394
|
+
);
|
|
1395
|
+
this.closedCaptionsTasks.delete(currentKey);
|
|
1396
|
+
}, visibilityDurationMs);
|
|
1397
|
+
this.closedCaptionsTasks.set(currentKey, taskId);
|
|
1398
|
+
|
|
1399
|
+
// cancel the cleanup tasks for the closed captions that are no longer in the queue
|
|
1400
|
+
for (let i = 0; i < nextQueue.length - maxVisibleCaptions; i++) {
|
|
1401
|
+
const key = keyOf(nextQueue[i]);
|
|
1402
|
+
const task = this.closedCaptionsTasks.get(key);
|
|
1403
|
+
clearTimeout(task);
|
|
1404
|
+
this.closedCaptionsTasks.delete(key);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// trim the queue
|
|
1409
|
+
return nextQueue.slice(-maxVisibleCaptions);
|
|
1410
|
+
});
|
|
1411
|
+
};
|
|
1302
1412
|
}
|
|
@@ -967,4 +967,105 @@ describe('CallState', () => {
|
|
|
967
967
|
expect(state['orphanedTracks'].length).toBe(0);
|
|
968
968
|
});
|
|
969
969
|
});
|
|
970
|
+
|
|
971
|
+
describe('closed captions', () => {
|
|
972
|
+
it('should add closed captions to the queue', () => {
|
|
973
|
+
const state = new CallState();
|
|
974
|
+
state.updateFromEvent({
|
|
975
|
+
type: 'call.closed_caption',
|
|
976
|
+
// @ts-expect-error incomplete data
|
|
977
|
+
closed_caption: {
|
|
978
|
+
speaker_id: '123',
|
|
979
|
+
text: 'Hello world',
|
|
980
|
+
start_time: '2021-01-01T00:00:00.000Z',
|
|
981
|
+
end_time: '2021-01-01T00:02:00.000Z',
|
|
982
|
+
},
|
|
983
|
+
});
|
|
984
|
+
expect(state.closedCaptions.length).toBe(1);
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
it('should maintain predefined queue size', () => {
|
|
988
|
+
const state = new CallState();
|
|
989
|
+
state.updateClosedCaptionSettings({ maxVisibleCaptions: 2 });
|
|
990
|
+
for (let i = 0; i < 5; i++) {
|
|
991
|
+
state.updateFromEvent({
|
|
992
|
+
type: 'call.closed_caption',
|
|
993
|
+
// @ts-expect-error incomplete data
|
|
994
|
+
closed_caption: {
|
|
995
|
+
speaker_id: `123-${i}`,
|
|
996
|
+
text: `Hello world ${i}`,
|
|
997
|
+
start_time: '2021-01-01T00:00:00.000Z',
|
|
998
|
+
end_time: '2021-01-01T00:02:00.000Z',
|
|
999
|
+
},
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
expect(state.closedCaptions.length).toBe(2);
|
|
1003
|
+
expect(state['closedCaptionsTasks'].size).toBe(2);
|
|
1004
|
+
expect(state.closedCaptions.map((cc) => cc.text)).toEqual([
|
|
1005
|
+
'Hello world 3',
|
|
1006
|
+
'Hello world 4',
|
|
1007
|
+
]);
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
it('should remove stale captions from the queue', () => {
|
|
1011
|
+
const state = new CallState();
|
|
1012
|
+
vi.useFakeTimers();
|
|
1013
|
+
state.updateFromEvent({
|
|
1014
|
+
type: 'call.closed_caption',
|
|
1015
|
+
// @ts-expect-error incomplete data
|
|
1016
|
+
closed_caption: {
|
|
1017
|
+
speaker_id: `123`,
|
|
1018
|
+
text: `Hello world`,
|
|
1019
|
+
start_time: '2021-01-01T00:00:00.000Z',
|
|
1020
|
+
end_time: '2021-01-01T00:02:00.000Z',
|
|
1021
|
+
},
|
|
1022
|
+
});
|
|
1023
|
+
expect(state.closedCaptions.length).toBe(1);
|
|
1024
|
+
expect(state['closedCaptionsTasks'].size).toBe(1);
|
|
1025
|
+
|
|
1026
|
+
vi.runAllTimers();
|
|
1027
|
+
expect(state.closedCaptions.length).toBe(0);
|
|
1028
|
+
expect(state['closedCaptionsTasks'].size).toBe(0);
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
it('should remove stale captions from the queue after timer runs', () => {
|
|
1032
|
+
const state = new CallState();
|
|
1033
|
+
state.updateClosedCaptionSettings({ visibilityDurationMs: 100 });
|
|
1034
|
+
vi.useFakeTimers();
|
|
1035
|
+
state.updateFromEvent({
|
|
1036
|
+
type: 'call.closed_caption',
|
|
1037
|
+
// @ts-expect-error incomplete data
|
|
1038
|
+
closed_caption: {
|
|
1039
|
+
speaker_id: `123`,
|
|
1040
|
+
text: `Hello world`,
|
|
1041
|
+
start_time: '2021-01-01T00:00:00.000Z',
|
|
1042
|
+
end_time: '2021-01-01T00:02:00.000Z',
|
|
1043
|
+
},
|
|
1044
|
+
});
|
|
1045
|
+
expect(state.closedCaptions.length).toBe(1);
|
|
1046
|
+
expect(state['closedCaptionsTasks'].size).toBe(1);
|
|
1047
|
+
|
|
1048
|
+
vi.advanceTimersByTime(101);
|
|
1049
|
+
expect(state.closedCaptions.length).toBe(0);
|
|
1050
|
+
expect(state['closedCaptionsTasks'].size).toBe(0);
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
it('dispose cancels all cleanup tasks', () => {
|
|
1054
|
+
const state = new CallState();
|
|
1055
|
+
state.updateFromEvent({
|
|
1056
|
+
type: 'call.closed_caption',
|
|
1057
|
+
// @ts-expect-error incomplete data
|
|
1058
|
+
closed_caption: {
|
|
1059
|
+
speaker_id: `123`,
|
|
1060
|
+
text: `Hello world`,
|
|
1061
|
+
start_time: '2021-01-01T00:00:00.000Z',
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
expect(state.closedCaptions.length).toBe(1);
|
|
1065
|
+
expect(state['closedCaptionsTasks'].size).toBe(1);
|
|
1066
|
+
|
|
1067
|
+
state.dispose();
|
|
1068
|
+
expect(state['closedCaptionsTasks'].size).toBe(0);
|
|
1069
|
+
});
|
|
1070
|
+
});
|
|
970
1071
|
});
|
package/src/store/rxUtils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { combineLatest, Observable, Subject } from 'rxjs';
|
|
1
|
+
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
|
|
2
2
|
import { withoutConcurrency } from '../helpers/concurrency';
|
|
3
3
|
import { getLogger } from '../logger';
|
|
4
4
|
|
|
@@ -59,6 +59,28 @@ export const setCurrentValue = <T>(subject: Subject<T>, update: Patch<T>) => {
|
|
|
59
59
|
return next;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Updates the value of the provided Subject and returns the previous value
|
|
64
|
+
* and a function to roll back the update.
|
|
65
|
+
* This is useful when you want to optimistically update a value
|
|
66
|
+
* and roll back the update if an error occurs.
|
|
67
|
+
*
|
|
68
|
+
* @param subject the subject to update.
|
|
69
|
+
* @param update the update to apply to the subject.
|
|
70
|
+
*/
|
|
71
|
+
export const updateValue = <T>(
|
|
72
|
+
subject: BehaviorSubject<T>,
|
|
73
|
+
update: Patch<T>,
|
|
74
|
+
) => {
|
|
75
|
+
const lastValue = subject.getValue();
|
|
76
|
+
const value = setCurrentValue(subject, update);
|
|
77
|
+
return {
|
|
78
|
+
lastValue,
|
|
79
|
+
value,
|
|
80
|
+
rollback: () => setCurrentValue(subject, lastValue),
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
62
84
|
/**
|
|
63
85
|
* Creates a subscription and returns a function to unsubscribe.
|
|
64
86
|
*
|
package/src/types.ts
CHANGED
|
@@ -122,6 +122,21 @@ export type ParticipantPin = {
|
|
|
122
122
|
pinnedAt: number;
|
|
123
123
|
};
|
|
124
124
|
|
|
125
|
+
export type ClosedCaptionsSettings = {
|
|
126
|
+
/**
|
|
127
|
+
* The time in milliseconds to keep a closed caption in the state (visible).
|
|
128
|
+
* Default is 2700 ms.
|
|
129
|
+
*/
|
|
130
|
+
visibilityDurationMs?: number;
|
|
131
|
+
/**
|
|
132
|
+
* The maximum number of closed captions to keep in the state (visible).
|
|
133
|
+
* When the maximum number is reached, the oldest closed caption is removed.
|
|
134
|
+
*
|
|
135
|
+
* Default is 2.
|
|
136
|
+
*/
|
|
137
|
+
maxVisibleCaptions?: number;
|
|
138
|
+
};
|
|
139
|
+
|
|
125
140
|
/**
|
|
126
141
|
* A partial representation of the StreamVideoParticipant.
|
|
127
142
|
*/
|
|
@@ -147,7 +162,7 @@ export type SubscriptionChanges = {
|
|
|
147
162
|
};
|
|
148
163
|
|
|
149
164
|
/**
|
|
150
|
-
* A preferred codec to use when publishing a video track.
|
|
165
|
+
* A preferred codec to use when publishing a video or audio track.
|
|
151
166
|
* @internal
|
|
152
167
|
*/
|
|
153
168
|
export type PreferredCodec = 'vp8' | 'h264' | 'vp9' | 'av1';
|
|
@@ -156,41 +171,31 @@ export type PreferredCodec = 'vp8' | 'h264' | 'vp9' | 'av1';
|
|
|
156
171
|
* A collection of track publication options.
|
|
157
172
|
* @internal
|
|
158
173
|
*/
|
|
159
|
-
export type
|
|
174
|
+
export type ClientPublishOptions = {
|
|
160
175
|
/**
|
|
161
176
|
* The preferred codec to use when publishing the video stream.
|
|
162
177
|
*/
|
|
163
178
|
preferredCodec?: PreferredCodec;
|
|
164
179
|
/**
|
|
165
|
-
*
|
|
166
|
-
* This will override the preferred codec and the internal codec selection logic.
|
|
167
|
-
* Use with caution.
|
|
168
|
-
*/
|
|
169
|
-
forceCodec?: PreferredCodec;
|
|
170
|
-
/**
|
|
171
|
-
* When using a preferred codec, force the use of a single codec.
|
|
172
|
-
* Enabling this, it will remove all other supported codecs from the SDP.
|
|
173
|
-
* Defaults to false.
|
|
180
|
+
* The fmtp line for the video codec.
|
|
174
181
|
*/
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* The preferred scalability to use when publishing the video stream.
|
|
178
|
-
* Applicable only for SVC codecs.
|
|
179
|
-
*/
|
|
180
|
-
scalabilityMode?: string;
|
|
182
|
+
fmtpLine?: string;
|
|
181
183
|
/**
|
|
182
184
|
* The preferred bitrate to use when publishing the video stream.
|
|
183
185
|
*/
|
|
184
186
|
preferredBitrate?: number;
|
|
185
|
-
/**
|
|
186
|
-
* The preferred downscale factor to use when publishing the video stream
|
|
187
|
-
* in simulcast mode (non-SVC).
|
|
188
|
-
*/
|
|
189
|
-
bitrateDownscaleFactor?: number;
|
|
190
187
|
/**
|
|
191
188
|
* The maximum number of simulcast layers to use when publishing the video stream.
|
|
192
189
|
*/
|
|
193
190
|
maxSimulcastLayers?: number;
|
|
191
|
+
/**
|
|
192
|
+
* The preferred subscription (incoming video stream) codec.
|
|
193
|
+
*/
|
|
194
|
+
subscriberCodec?: PreferredCodec;
|
|
195
|
+
/**
|
|
196
|
+
* The fmtp line for the subscriber codec.
|
|
197
|
+
*/
|
|
198
|
+
subscriberFmtpLine?: string;
|
|
194
199
|
/**
|
|
195
200
|
* Screen share settings.
|
|
196
201
|
*/
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns an SDP with DTX enabled or disabled.
|
|
3
|
-
*/
|
|
4
|
-
export declare const toggleDtx: (sdp: string, enable: boolean) => string;
|
|
5
|
-
/**
|
|
6
|
-
* Returns and SDP with all the codecs except the given codec removed.
|
|
7
|
-
*/
|
|
8
|
-
export declare const preserveCodec: (sdp: string, mid: string, codec: RTCRtpCodec) => string;
|
|
9
|
-
/**
|
|
10
|
-
* Enables high-quality audio through SDP munging for the given trackMid.
|
|
11
|
-
*
|
|
12
|
-
* @param sdp the SDP to munge.
|
|
13
|
-
* @param trackMid the trackMid.
|
|
14
|
-
* @param maxBitrate the max bitrate to set.
|
|
15
|
-
*/
|
|
16
|
-
export declare const enableHighQualityAudio: (sdp: string, trackMid: string, maxBitrate?: number) => string;
|
|
17
|
-
/**
|
|
18
|
-
* Extracts the mid from the transceiver or the SDP.
|
|
19
|
-
*
|
|
20
|
-
* @param transceiver the transceiver.
|
|
21
|
-
* @param transceiverInitIndex the index of the transceiver in the transceiver's init array.
|
|
22
|
-
* @param sdp the SDP.
|
|
23
|
-
*/
|
|
24
|
-
export declare const extractMid: (transceiver: RTCRtpTransceiver, transceiverInitIndex: number, sdp: string | undefined) => string;
|