@pexip/media 19.1.0 → 20.0.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 +62 -0
- package/dist/audioMixingProcessor.d.ts +1 -1
- package/dist/audioMixingProcessor.js +5 -5
- package/dist/audioProcessor.d.ts +1 -1
- package/dist/audioProcessor.js +4 -2
- package/dist/baseLogger.d.ts +15 -6
- package/dist/baseLogger.js +13 -7
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/displayMedia.d.ts +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +8 -7
- package/dist/logger.js +1 -4
- package/dist/media.d.ts +6 -2
- package/dist/media.js +86 -44
- package/dist/previewController.js +3 -4
- package/dist/signals.d.ts +4 -4
- package/dist/signals.js +8 -5
- package/dist/status.d.ts +2 -1
- package/dist/status.js +8 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/typeGuard.d.ts +2 -1
- package/dist/typeGuard.js +3 -0
- package/dist/types.d.ts +3 -3
- package/dist/userMedia.d.ts +8 -1
- package/dist/userMedia.js +357 -20
- package/dist/utils.d.ts +10 -8
- package/dist/utils.js +81 -37
- package/dist/videoProcessor.d.ts +5 -3
- package/dist/videoProcessor.js +53 -60
- package/package.json +22 -11
package/dist/utils.js
CHANGED
|
@@ -3,22 +3,22 @@ import { calculateMaxBlurPass } from '@pexip/media-processor';
|
|
|
3
3
|
import { hasOwn, assert, isEmpty } from '@pexip/utils';
|
|
4
4
|
import { internalSignals } from './signals';
|
|
5
5
|
export const makeDeriveDeviceStatus = (constraints) => (audio, video, both) => {
|
|
6
|
-
if (constraints.audio
|
|
7
|
-
if (constraints.video
|
|
6
|
+
if (constraints.audio) {
|
|
7
|
+
if (constraints.video) {
|
|
8
8
|
return both;
|
|
9
9
|
}
|
|
10
10
|
return audio;
|
|
11
11
|
}
|
|
12
|
-
if (constraints.video
|
|
12
|
+
if (constraints.video) {
|
|
13
13
|
return video;
|
|
14
14
|
}
|
|
15
15
|
return both;
|
|
16
16
|
};
|
|
17
17
|
export const createMediaTrack = (trackInit) => {
|
|
18
|
-
|
|
18
|
+
const trackConsistency = trackInit.track === undefined ||
|
|
19
19
|
(trackInit.kind === trackInit.input?.kind &&
|
|
20
|
-
trackInit.kind === toMediaDeviceInputKind(trackInit.track)
|
|
21
|
-
|
|
20
|
+
trackInit.kind === toMediaDeviceInputKind(trackInit.track));
|
|
21
|
+
assert(trackConsistency, `Inconsistent track kind: ${trackInit.input?.kind} ${trackInit.track && toMediaDeviceInputKind(trackInit.track)} vs ${trackInit.kind}`);
|
|
22
22
|
const currentConstraints = typeof trackInit.constraints === 'boolean' ? {} : trackInit.constraints;
|
|
23
23
|
// Find the original source track through the linked list
|
|
24
24
|
let sourceMediaTrack = trackInit.previousMediaTrack;
|
|
@@ -52,7 +52,9 @@ export const createMediaTrack = (trackInit) => {
|
|
|
52
52
|
return trackInit.track;
|
|
53
53
|
},
|
|
54
54
|
get input() {
|
|
55
|
-
return trackInit.
|
|
55
|
+
return trackInit.track?.readyState !== 'live'
|
|
56
|
+
? undefined
|
|
57
|
+
: trackInit.input;
|
|
56
58
|
},
|
|
57
59
|
get expectedInput() {
|
|
58
60
|
return trackInit.expectedInput;
|
|
@@ -89,11 +91,12 @@ export const createMediaTrack = (trackInit) => {
|
|
|
89
91
|
getConstraints() {
|
|
90
92
|
return getConstraints();
|
|
91
93
|
},
|
|
92
|
-
clone() {
|
|
94
|
+
clone(signals) {
|
|
93
95
|
return createMediaTrack({
|
|
94
96
|
...trackInit,
|
|
95
97
|
constraints: getConstraints(),
|
|
96
98
|
track: trackInit.track?.clone(),
|
|
99
|
+
signals,
|
|
97
100
|
});
|
|
98
101
|
},
|
|
99
102
|
async applyConstraints(constraints) {
|
|
@@ -157,6 +160,9 @@ export const createMediaTrack = (trackInit) => {
|
|
|
157
160
|
export const createTrackProcessingPipeline = (processors) => async (track) => {
|
|
158
161
|
let lastTrack = track;
|
|
159
162
|
for (const process of processors) {
|
|
163
|
+
if (lastTrack.track?.readyState !== 'live') {
|
|
164
|
+
return lastTrack;
|
|
165
|
+
}
|
|
160
166
|
lastTrack = await process(lastTrack);
|
|
161
167
|
}
|
|
162
168
|
return lastTrack;
|
|
@@ -188,7 +194,7 @@ export const findExpectedInput = (devices, constraints, input, kind) => {
|
|
|
188
194
|
* A utility function to check if the provided track is muted. There are
|
|
189
195
|
* 2 factors to be considered: `MediaStreamTrack['muted']` and `MediaStreamTrack['enabled']`.
|
|
190
196
|
*
|
|
191
|
-
* ```
|
|
197
|
+
* ```md
|
|
192
198
|
* | muted \ enabled | true | false |
|
|
193
199
|
* |-----------------| ----- | ----- |
|
|
194
200
|
* | true | true | true |
|
|
@@ -335,23 +341,23 @@ export const buildMedia = (mediaInit, onDevicesChanged = internalSignals.onDevic
|
|
|
335
341
|
return props.originalConstraints;
|
|
336
342
|
},
|
|
337
343
|
isAudioInputUnavailable() {
|
|
338
|
-
return
|
|
339
|
-
props.devices.size('audioinput') === 0);
|
|
344
|
+
return !props.devices.anyAuthorizedDevice('audioinput');
|
|
340
345
|
},
|
|
341
346
|
isVideoInputUnavailable() {
|
|
342
|
-
return
|
|
343
|
-
props.devices.size('videoinput') === 0);
|
|
347
|
+
return !props.devices.anyAuthorizedDevice('videoinput');
|
|
344
348
|
},
|
|
345
349
|
setOriginalConstraints(constraints) {
|
|
346
350
|
props.originalConstraints = constraints;
|
|
347
351
|
},
|
|
348
352
|
requestedAudio() {
|
|
349
353
|
return (Boolean(props.audioTrack?.track) ||
|
|
350
|
-
Boolean(props.originalConstraints.audio)
|
|
354
|
+
(Boolean(props.originalConstraints.audio) &&
|
|
355
|
+
props.devices.size('audioinput') > 0));
|
|
351
356
|
},
|
|
352
357
|
requestedVideo() {
|
|
353
358
|
return (Boolean(props.videoTrack?.track) ||
|
|
354
|
-
Boolean(props.originalConstraints.video)
|
|
359
|
+
(Boolean(props.originalConstraints.video) &&
|
|
360
|
+
props.devices.size('videoinput') > 0));
|
|
355
361
|
},
|
|
356
362
|
muteAudio(mute) {
|
|
357
363
|
if (!props.audioTrack) {
|
|
@@ -378,7 +384,9 @@ export const buildMedia = (mediaInit, onDevicesChanged = internalSignals.onDevic
|
|
|
378
384
|
async release() {
|
|
379
385
|
unsubscribe?.();
|
|
380
386
|
unsubscribe = undefined;
|
|
381
|
-
|
|
387
|
+
for (const unsubscribe of trackSubscriptions.values()) {
|
|
388
|
+
unsubscribe();
|
|
389
|
+
}
|
|
382
390
|
trackSubscriptions.clear();
|
|
383
391
|
await Promise.all([
|
|
384
392
|
props.audioTrack?.release(),
|
|
@@ -389,12 +397,12 @@ export const buildMedia = (mediaInit, onDevicesChanged = internalSignals.onDevic
|
|
|
389
397
|
audio: props.audioTrack?.getSettings(),
|
|
390
398
|
video: props.videoTrack?.getSettings(),
|
|
391
399
|
}),
|
|
392
|
-
clone() {
|
|
400
|
+
clone(signals) {
|
|
393
401
|
const tracks = [props.audioTrack, props.videoTrack].flatMap(track => {
|
|
394
402
|
if (!track?.source) {
|
|
395
403
|
return [];
|
|
396
404
|
}
|
|
397
|
-
const cloned = track.source.clone();
|
|
405
|
+
const cloned = track.source.clone(signals);
|
|
398
406
|
// Restore the enabled state for all cloned track
|
|
399
407
|
cloned.mute(false);
|
|
400
408
|
return cloned;
|
|
@@ -517,6 +525,11 @@ export const VIDEO_SETTINGS_KEYS = [
|
|
|
517
525
|
export const MIXING_SETTINGS_KEYS = [
|
|
518
526
|
'mixWithAdditionalMedia',
|
|
519
527
|
];
|
|
528
|
+
export const PAN_TILT_ZOOM_SETTINGS_KEYS = [
|
|
529
|
+
'pan',
|
|
530
|
+
'tilt',
|
|
531
|
+
'zoom',
|
|
532
|
+
];
|
|
520
533
|
export const hasSettingsChanged = (keysToLookFor) => {
|
|
521
534
|
const cache = {};
|
|
522
535
|
return (settingsA, settingsB) => {
|
|
@@ -545,6 +558,28 @@ export const hasSettingsChanged = (keysToLookFor) => {
|
|
|
545
558
|
return cache.result;
|
|
546
559
|
};
|
|
547
560
|
};
|
|
561
|
+
export const isApplyingRenderEffect = (effect) => {
|
|
562
|
+
switch (effect) {
|
|
563
|
+
case 'blur':
|
|
564
|
+
case 'overlay':
|
|
565
|
+
return true;
|
|
566
|
+
default:
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
export const getSettingsFromKeys = (keys, settings) => {
|
|
571
|
+
if (!settings) {
|
|
572
|
+
return settings;
|
|
573
|
+
}
|
|
574
|
+
const result = {};
|
|
575
|
+
for (const key of keys) {
|
|
576
|
+
if (settings[key] !== undefined) {
|
|
577
|
+
// @ts-expect-error --- Type issue and need to be fixed when we have time
|
|
578
|
+
result[key] = settings[key];
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return result;
|
|
582
|
+
};
|
|
548
583
|
export const diffSettings = (keysToLookFor) => {
|
|
549
584
|
const cache = {};
|
|
550
585
|
return (settingsA, settingsB) => {
|
|
@@ -560,7 +595,16 @@ export const diffSettings = (keysToLookFor) => {
|
|
|
560
595
|
return cache.diff;
|
|
561
596
|
}
|
|
562
597
|
if (settingsA === undefined || settingsB === undefined) {
|
|
563
|
-
|
|
598
|
+
const settings = settingsA ?? settingsB;
|
|
599
|
+
for (const key of keysToLookFor) {
|
|
600
|
+
if (settings?.[key] !== undefined) {
|
|
601
|
+
if (cache.diff === undefined) {
|
|
602
|
+
cache.diff = {};
|
|
603
|
+
}
|
|
604
|
+
// @ts-expect-error --- Type issue and need to be fixed when we have time
|
|
605
|
+
cache.diff[key] = settings[key];
|
|
606
|
+
}
|
|
607
|
+
}
|
|
564
608
|
return cache.diff;
|
|
565
609
|
}
|
|
566
610
|
for (const key of keysToLookFor) {
|
|
@@ -625,6 +669,7 @@ export const refineMediaConstraints = (state) => {
|
|
|
625
669
|
switch (state.permission) {
|
|
626
670
|
case 'denied':
|
|
627
671
|
return false;
|
|
672
|
+
case 'prompt':
|
|
628
673
|
case 'granted': {
|
|
629
674
|
const requestNeeded = shouldRequestDevice({
|
|
630
675
|
kind: state.kind,
|
|
@@ -642,7 +687,6 @@ export const refineMediaConstraints = (state) => {
|
|
|
642
687
|
}
|
|
643
688
|
return state.request;
|
|
644
689
|
}
|
|
645
|
-
case 'prompt':
|
|
646
690
|
default:
|
|
647
691
|
return state.request;
|
|
648
692
|
}
|
|
@@ -650,25 +694,25 @@ export const refineMediaConstraints = (state) => {
|
|
|
650
694
|
export const createMediaProcessor = ({ audioProcessors, videoProcessors, onProcessingError, }) => {
|
|
651
695
|
const processAudioTrack = createTrackProcessingPipeline(audioProcessors);
|
|
652
696
|
const processVideoTrack = createTrackProcessingPipeline(videoProcessors);
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
assert(false, `Unexpected track kind: ${track.kind}`);
|
|
663
|
-
}
|
|
697
|
+
const processTrack = async (track) => {
|
|
698
|
+
try {
|
|
699
|
+
switch (track.kind) {
|
|
700
|
+
case 'audioinput':
|
|
701
|
+
return await processAudioTrack(track);
|
|
702
|
+
case 'videoinput':
|
|
703
|
+
return await processVideoTrack(track);
|
|
704
|
+
default:
|
|
705
|
+
assert(false, `Unexpected track kind: ${track.kind}`);
|
|
664
706
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
return track;
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
if (error instanceof Error) {
|
|
710
|
+
onProcessingError?.(error, track);
|
|
670
711
|
}
|
|
671
|
-
|
|
712
|
+
return track;
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
return async (newTracks) => {
|
|
672
716
|
return await Promise.all(newTracks.map(track => processTrack(track)));
|
|
673
717
|
};
|
|
674
718
|
};
|
package/dist/videoProcessor.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { VideoProcessor, SegmentationTransform, SegmentationModel } from '@pexip/media-processor';
|
|
1
|
+
import type { VideoProcessor, SegmentationTransform, SegmentationModel, RenderBackend } from '@pexip/media-processor';
|
|
2
2
|
import type { MediaDeviceRequest } from '@pexip/media-control';
|
|
3
3
|
import type { TrackProcessor, VideoRenderParams, Segmenters, VideoStreamTrackProcessorAPIs, VideoContentHint } from './types';
|
|
4
4
|
interface ProcessorDeps {
|
|
@@ -24,6 +24,8 @@ interface VideoStreamProcessOptions extends Partial<VideoRenderParams>, Omit<Pro
|
|
|
24
24
|
width?: number;
|
|
25
25
|
height?: number;
|
|
26
26
|
label?: string;
|
|
27
|
+
dynamicProcessingDimensions?: () => boolean;
|
|
28
|
+
gpuAPI?: () => RenderBackend;
|
|
27
29
|
}
|
|
28
30
|
interface VideoStreamProcessProps extends Partial<VideoRenderParams>, Required<ProcessorDeps> {
|
|
29
31
|
hasInitialized: boolean;
|
|
@@ -32,6 +34,6 @@ interface VideoStreamProcessProps extends Partial<VideoRenderParams>, Required<P
|
|
|
32
34
|
declare const FEATURE_KEYS: readonly ["backgroundBlurAmount", "backgroundImageUrl", "maskCombineRatio", "edgeBlurAmount", "foregroundThreshold", "frameRate", "videoSegmentation", "videoSegmentationModel", "width", "height", "pan", "tilt", "zoom", "contentHint"];
|
|
33
35
|
type FeaturePropKeys = (typeof FEATURE_KEYS)[number];
|
|
34
36
|
type FeatureProps = Pick<Partial<VideoStreamProcessProps>, FeaturePropKeys>;
|
|
35
|
-
export declare const updateFeatureProps: (constraints: MediaDeviceRequest[
|
|
36
|
-
export declare const createVideoStreamProcess: ({ trackProcessorAPI, processingWidth, processingHeight, shouldEnable, frameRate, videoSegmentation, foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label, ...options }: VideoStreamProcessOptions) => TrackProcessor;
|
|
37
|
+
export declare const updateFeatureProps: (constraints: MediaDeviceRequest["video"], props: FeatureProps) => FeatureProps;
|
|
38
|
+
export declare const createVideoStreamProcess: ({ trackProcessorAPI, processingWidth, processingHeight, shouldEnable, frameRate, videoSegmentation, foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label, dynamicProcessingDimensions, gpuAPI, ...options }: VideoStreamProcessOptions) => TrackProcessor;
|
|
37
39
|
export {};
|
package/dist/videoProcessor.js
CHANGED
|
@@ -30,7 +30,8 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
30
30
|
const [[feature = ''] = []] = extracted[key];
|
|
31
31
|
if (isVideoContentHint(feature) && props[key] !== feature) {
|
|
32
32
|
props[key] = feature;
|
|
33
|
-
|
|
33
|
+
accm[key] = feature;
|
|
34
|
+
return accm;
|
|
34
35
|
}
|
|
35
36
|
return accm;
|
|
36
37
|
}
|
|
@@ -38,7 +39,8 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
38
39
|
const [[feature] = []] = extracted[key];
|
|
39
40
|
if (isRenderEffects(feature) && props[key] !== feature) {
|
|
40
41
|
props[key] = feature;
|
|
41
|
-
|
|
42
|
+
accm[key] = feature;
|
|
43
|
+
return accm;
|
|
42
44
|
}
|
|
43
45
|
return accm;
|
|
44
46
|
}
|
|
@@ -46,7 +48,8 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
46
48
|
const [[feature] = []] = extracted[key];
|
|
47
49
|
if (isSegmentationModel(feature) && props[key] !== feature) {
|
|
48
50
|
props[key] = feature;
|
|
49
|
-
|
|
51
|
+
accm[key] = feature;
|
|
52
|
+
return accm;
|
|
50
53
|
}
|
|
51
54
|
return accm;
|
|
52
55
|
}
|
|
@@ -54,7 +57,8 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
54
57
|
const [[feature] = []] = extracted[key];
|
|
55
58
|
if (feature) {
|
|
56
59
|
props[key] = feature;
|
|
57
|
-
|
|
60
|
+
accm[key] = feature;
|
|
61
|
+
return accm;
|
|
58
62
|
}
|
|
59
63
|
return accm;
|
|
60
64
|
}
|
|
@@ -70,10 +74,8 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
70
74
|
const value = getValueFromConstrainNumber(feature);
|
|
71
75
|
if (props[key] !== value) {
|
|
72
76
|
props[key] = value;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
[key]: value,
|
|
76
|
-
};
|
|
77
|
+
accm[key] = value;
|
|
78
|
+
return accm;
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
return accm;
|
|
@@ -84,7 +86,8 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
84
86
|
const [feature] = extracted[key];
|
|
85
87
|
if (feature !== undefined && props[key] !== feature) {
|
|
86
88
|
props[key] = feature;
|
|
87
|
-
|
|
89
|
+
accm[key] = feature;
|
|
90
|
+
return accm;
|
|
88
91
|
}
|
|
89
92
|
return accm;
|
|
90
93
|
}
|
|
@@ -92,7 +95,7 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
92
95
|
}, {});
|
|
93
96
|
};
|
|
94
97
|
const applyFeatures = (transformer, features) => {
|
|
95
|
-
|
|
98
|
+
for (const key in features) {
|
|
96
99
|
const k = key;
|
|
97
100
|
switch (k) {
|
|
98
101
|
case 'edgeBlurAmount':
|
|
@@ -102,66 +105,30 @@ const applyFeatures = (transformer, features) => {
|
|
|
102
105
|
if (value !== undefined) {
|
|
103
106
|
transformer[k] = value;
|
|
104
107
|
}
|
|
105
|
-
|
|
108
|
+
break;
|
|
106
109
|
}
|
|
107
110
|
case 'backgroundBlurAmount': {
|
|
108
111
|
const value = features[k];
|
|
109
112
|
if (value !== undefined) {
|
|
110
113
|
transformer[k] = getBlurKernelSize(value, transformer.height);
|
|
111
114
|
}
|
|
112
|
-
|
|
115
|
+
break;
|
|
113
116
|
}
|
|
114
117
|
case 'videoSegmentation': {
|
|
115
118
|
const value = features[k];
|
|
116
119
|
if (value && value !== transformer.effects) {
|
|
117
120
|
transformer.effects = value;
|
|
118
121
|
}
|
|
119
|
-
|
|
122
|
+
break;
|
|
120
123
|
}
|
|
121
124
|
case 'backgroundImageUrl': {
|
|
122
125
|
const value = features[k];
|
|
123
126
|
if (value && value !== transformer.backgroundImageUrl) {
|
|
124
127
|
transformer.backgroundImageUrl = value;
|
|
125
128
|
}
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
default: {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
};
|
|
134
|
-
const adjustResolution = async (track, features, processingSize) => {
|
|
135
|
-
if (features.videoSegmentation) {
|
|
136
|
-
const videoSettings = track.getSettings();
|
|
137
|
-
const constraints = updateFeatureProps(track.getConstraints(), {});
|
|
138
|
-
switch (features.videoSegmentation) {
|
|
139
|
-
case 'blur':
|
|
140
|
-
case 'overlay': {
|
|
141
|
-
if (videoSettings?.height !== processingSize.height) {
|
|
142
|
-
try {
|
|
143
|
-
await track.applyConstraints({
|
|
144
|
-
width: processingSize.width,
|
|
145
|
-
height: processingSize.height,
|
|
146
|
-
});
|
|
147
|
-
const postVideoSettings = track.getSettings();
|
|
148
|
-
if (postVideoSettings?.height !== processingSize.height) {
|
|
149
|
-
// Workaround Firefox 16:9 ratio https://bugzilla.mozilla.org/show_bug.cgi?id=1193640
|
|
150
|
-
await track.applyConstraints({ height: 720 });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
// Workaround Firefox 16:9 ratio https://bugzilla.mozilla.org/show_bug.cgi?id=1193640
|
|
155
|
-
await track.applyConstraints({ height: 720 });
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
129
|
break;
|
|
159
130
|
}
|
|
160
|
-
|
|
161
|
-
if (constraints.height &&
|
|
162
|
-
constraints.height !== videoSettings?.height) {
|
|
163
|
-
await track.applyConstraints({ height: constraints.height });
|
|
164
|
-
}
|
|
131
|
+
default: {
|
|
165
132
|
break;
|
|
166
133
|
}
|
|
167
134
|
}
|
|
@@ -178,7 +145,8 @@ export const createVideoStreamProcess = ({ trackProcessorAPI = () => 'stream', p
|
|
|
178
145
|
//backgroundBlurAmount,
|
|
179
146
|
videoSegmentation,
|
|
180
147
|
//edgeBlurAmount,
|
|
181
|
-
foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label = PROCESSOR_LABELS.VideoProcessor,
|
|
148
|
+
foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label = PROCESSOR_LABELS.VideoProcessor, dynamicProcessingDimensions = () => false, gpuAPI = () => 'webgl', // dynamically changing the config will not be picked up, not a priority for now
|
|
149
|
+
...options }) => {
|
|
182
150
|
const videoSegmentationModel = options.videoSegmentationModel ?? 'selfie';
|
|
183
151
|
const segmenter = options.segmenters[videoSegmentationModel];
|
|
184
152
|
if (!segmenter) {
|
|
@@ -235,14 +203,34 @@ foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label
|
|
|
235
203
|
return prevMediaTrack;
|
|
236
204
|
}
|
|
237
205
|
if (!props.hasInitialized) {
|
|
238
|
-
|
|
206
|
+
if (dynamicProcessingDimensions()) {
|
|
207
|
+
const { width, height } = prevMediaTrack.getSettings();
|
|
208
|
+
/**
|
|
209
|
+
* when the processor is closed, the segmenter is not killed, but the track is released.
|
|
210
|
+
* this means we can't listen on the track settings changes when we change quality
|
|
211
|
+
* we need to update instead of open if the segmenter is already running
|
|
212
|
+
*/
|
|
213
|
+
if (props.transformer.segmenter.status === 'new') {
|
|
214
|
+
await props.videoProcessor().open({
|
|
215
|
+
processingWidth: width,
|
|
216
|
+
processingHeight: height,
|
|
217
|
+
});
|
|
218
|
+
props.transformer.backend = gpuAPI();
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
props.transformer.update({
|
|
222
|
+
processingWidth: width,
|
|
223
|
+
processingHeight: height,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
await props.videoProcessor().open();
|
|
229
|
+
props.transformer.backend = gpuAPI();
|
|
230
|
+
}
|
|
239
231
|
props.hasInitialized = true;
|
|
240
232
|
}
|
|
241
233
|
applyFeatures(props.transformer, features);
|
|
242
|
-
await adjustResolution(prevMediaTrack, features, {
|
|
243
|
-
width: processingWidth,
|
|
244
|
-
height: processingHeight,
|
|
245
|
-
});
|
|
246
234
|
const model = features.videoSegmentationModel &&
|
|
247
235
|
props.segmenters[features.videoSegmentationModel];
|
|
248
236
|
if (features.videoSegmentationModel &&
|
|
@@ -292,10 +280,15 @@ foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label
|
|
|
292
280
|
return;
|
|
293
281
|
}
|
|
294
282
|
applyFeatures(props.transformer, features);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
height
|
|
298
|
-
|
|
283
|
+
if (dynamicProcessingDimensions()) {
|
|
284
|
+
const acceptedSettings = prevMediaTrack.source.getSettings();
|
|
285
|
+
if (acceptedSettings.width && acceptedSettings.height) {
|
|
286
|
+
props.transformer.update({
|
|
287
|
+
processingHeight: acceptedSettings.height,
|
|
288
|
+
processingWidth: acceptedSettings.width,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
299
292
|
const model = features.videoSegmentationModel &&
|
|
300
293
|
props.segmenters[features.videoSegmentationModel];
|
|
301
294
|
if (model &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pexip/media",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "20.0.0",
|
|
4
4
|
"description": "Home for media related stuff",
|
|
5
5
|
"homepage": "https://gitlab.com/pexip/zoo",
|
|
6
6
|
"bugs": "https://gitlab.com/pexip/zoo/issues",
|
|
@@ -14,6 +14,16 @@
|
|
|
14
14
|
"author": "Terrence Lam <terrence@tlam.dev>",
|
|
15
15
|
"module": "dist/index.js",
|
|
16
16
|
"types": "dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./*": {
|
|
23
|
+
"types": "./dist/*.d.ts",
|
|
24
|
+
"import": "./dist/*.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
17
27
|
"directories": {
|
|
18
28
|
"lib": "lib",
|
|
19
29
|
"test": "__tests__"
|
|
@@ -23,30 +33,31 @@
|
|
|
23
33
|
],
|
|
24
34
|
"scripts": {
|
|
25
35
|
"build": "pexip-bundler build",
|
|
36
|
+
"build:sourcemap": "pexip-bundler build --sourcemap",
|
|
26
37
|
"clean": "rm -fr dist",
|
|
27
38
|
"dev": "pexip-bundler dev",
|
|
28
|
-
"prepack": "yarn clean && yarn build
|
|
39
|
+
"prepack": "yarn clean && yarn build",
|
|
29
40
|
"test": "jest",
|
|
30
41
|
"check-format": "prettier --ignore-path=../../.prettierignore --check .",
|
|
31
42
|
"format": "prettier --ignore-path=../../.prettierignore --write .",
|
|
32
|
-
"lint": "yarn run -T
|
|
43
|
+
"lint": "yarn run -T biome lint",
|
|
33
44
|
"typecheck": "yarn tsc --noEmit -p ."
|
|
34
45
|
},
|
|
35
46
|
"dependencies": {
|
|
36
|
-
"@pexip/media-control": "
|
|
37
|
-
"@pexip/media-processor": "
|
|
38
|
-
"@pexip/signal": "16.
|
|
39
|
-
"@pexip/utils": "
|
|
47
|
+
"@pexip/media-control": "20.0.0",
|
|
48
|
+
"@pexip/media-processor": "20.0.0",
|
|
49
|
+
"@pexip/signal": "16.8.0",
|
|
50
|
+
"@pexip/utils": "17.0.0"
|
|
40
51
|
},
|
|
41
52
|
"devDependencies": {
|
|
42
53
|
"@jest/globals": "^29.7.0",
|
|
43
|
-
"@pexip/bundler": "
|
|
44
|
-
"@swc/core": "^1.
|
|
45
|
-
"@swc/jest": "^0.2.
|
|
54
|
+
"@pexip/bundler": "18.0.0",
|
|
55
|
+
"@swc/core": "^1.10.12",
|
|
56
|
+
"@swc/jest": "^0.2.37",
|
|
46
57
|
"jest": "^29.5.12",
|
|
47
58
|
"jest-junit": "^16.0.0",
|
|
48
59
|
"prettier": "^3.2.5",
|
|
49
|
-
"typescript": "~5.3
|
|
60
|
+
"typescript": "~5.7.3"
|
|
50
61
|
},
|
|
51
62
|
"publishConfig": {
|
|
52
63
|
"access": "public",
|