@pexip/media-processor 20.3.5 → 22.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 +109 -0
- package/README.md +17 -10
- package/api-docs/README.mdx +69 -23
- package/api-docs/functions/assertMediaStreamVideoTrack.mdx +13 -0
- package/api-docs/functions/createBenchmark.mdx +17 -5
- package/api-docs/functions/createFrameCallbackRequest.mdx +8 -2
- package/api-docs/functions/createMediaStreamTrackInputProcessAdaptor.mdx +10 -0
- package/api-docs/functions/createReadWritableStreamFallbackInputProcessAdaptor.mdx +17 -0
- package/api-docs/functions/createReadWritableStreamInputProcessAdaptor.mdx +10 -0
- package/api-docs/functions/createVideoProcessor.mdx +3 -3
- package/api-docs/functions/createWindowedStats.mdx +23 -0
- package/api-docs/functions/getDefaultInputProcessAdaptor.mdx +17 -0
- package/api-docs/functions/iou.mdx +19 -0
- package/api-docs/functions/isMediaStreamVideoTrack.mdx +13 -0
- package/api-docs/functions/labelComponents.mdx +20 -0
- package/api-docs/functions/nearestPowerOfTwo.mdx +21 -0
- package/api-docs/functions/playVideo.mdx +16 -0
- package/api-docs/functions/scoreComponent.mdx +22 -0
- package/api-docs/functions/setVideoElementSrc.mdx +14 -0
- package/api-docs/interfaces/Analyzer.mdx +4 -0
- package/api-docs/interfaces/Benchmark.mdx +26 -2
- package/api-docs/interfaces/FrameTransformStats.mdx +9 -0
- package/api-docs/interfaces/Gain.mdx +4 -0
- package/api-docs/interfaces/ImageSegmenterOptions.mdx +0 -2
- package/api-docs/interfaces/InputProcessAdaptor.mdx +28 -0
- package/api-docs/interfaces/Process.mdx +8 -3
- package/api-docs/interfaces/ProcessEventMute.mdx +6 -0
- package/api-docs/interfaces/ProcessWorkerEventOpened.mdx +7 -0
- package/api-docs/interfaces/ProcessWorkerEventStats.mdx +6 -0
- package/api-docs/interfaces/ProcessWorkerEventUpdated.mdx +7 -0
- package/api-docs/interfaces/ProcessorEvent.mdx +1 -1
- package/api-docs/interfaces/ProcessorOptions.mdx +0 -1
- package/api-docs/interfaces/ProcessorProcessOptions.mdx +3 -1
- package/api-docs/interfaces/ProcessorUpdateOptions.mdx +15 -0
- package/api-docs/interfaces/ReadWritableStream.mdx +12 -0
- package/api-docs/interfaces/Renderer.mdx +2 -2
- package/api-docs/interfaces/RendererOptions.mdx +25 -12
- package/api-docs/interfaces/Segmenter.mdx +89 -10
- package/api-docs/interfaces/SegmenterOptions.mdx +0 -1
- package/api-docs/interfaces/SelectionOptions.mdx +15 -0
- package/api-docs/interfaces/Stats.mdx +11 -0
- package/api-docs/interfaces/VideoProcessor.mdx +49 -5
- package/api-docs/interfaces/Weights.mdx +12 -0
- package/api-docs/type-aliases/OptionalKeys.mdx +10 -0
- package/api-docs/type-aliases/ProcessEvents.mdx +1 -0
- package/api-docs/type-aliases/ProcessInputType.mdx +3 -1
- package/api-docs/type-aliases/ProcessWorkerEvents.mdx +6 -4
- package/api-docs/type-aliases/ProcessorWorkerEvents.mdx +1 -1
- package/api-docs/type-aliases/Struct.mdx +9 -0
- package/api-docs/variables/BACKGROUND_THRESHOLD.mdx +3 -0
- package/api-docs/variables/DOWN_SAMPLE_FACTOR.mdx +3 -0
- package/api-docs/variables/EDGE_BLUR_AMOUNT.mdx +1 -1
- package/api-docs/variables/EXCLUDE_BYSTANDERS.mdx +3 -0
- package/api-docs/variables/FOREGROUND_THRESHOLD.mdx +1 -1
- package/api-docs/variables/FRAME_RATE.mdx +1 -1
- package/api-docs/variables/ImageFetchError.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_BLUR_AMOUNT.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_EDGE_BAND.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_INTENSITY.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_TIGHTNESS.mdx +3 -0
- package/api-docs/variables/MASK_COMBINE_RATIO.mdx +1 -1
- package/api-docs/variables/MORPH_DILATE_RADIUS_PX.mdx +3 -0
- package/api-docs/variables/MORPH_ERODE_RADIUS_PX.mdx +3 -0
- package/api-docs/variables/MORPH_PASS.mdx +3 -0
- package/api-docs/variables/MediaStreamTrackTransferable.mdx +5 -0
- package/api-docs/variables/PERSON_CENTER.mdx +3 -0
- package/api-docs/variables/RENDERER_OPTIONS_STRUCT.mdx +3 -0
- package/api-docs/variables/SIGMA_RANGE.mdx +3 -0
- package/api-docs/variables/SIGMA_SPACE.mdx +3 -0
- package/api-docs/variables/STRONG_EDGE_BLUR_AMOUNT.mdx +3 -0
- package/api-docs/variables/cloneImageRecord.mdx +19 -0
- package/api-docs/variables/compareImageRecords.mdx +18 -0
- package/api-docs/variables/createRemoteImageBitmap.mdx +1 -1
- package/api-docs/variables/getCanvasRenderingContext2D.mdx +14 -0
- package/api-docs/variables/getImageSize.mdx +13 -0
- package/api-docs/variables/isRenderOptionsKey.mdx +13 -0
- package/api-docs/variables/isValidGpuAPI.mdx +13 -0
- package/dist/common/backends/webgl/binaryMask.d.ts +8 -0
- package/dist/common/backends/webgl/binaryMask.js +95 -0
- package/dist/common/backends/webgl/blender.d.ts +2 -1
- package/dist/common/backends/webgl/blender.js +18 -15
- package/dist/common/backends/webgl/blur.d.ts +1 -1
- package/dist/common/backends/webgl/blur.js +23 -16
- package/dist/common/backends/webgl/canvas.js +9 -4
- package/dist/common/backends/webgl/diffAbsMask.d.ts +10 -0
- package/dist/common/backends/webgl/diffAbsMask.js +103 -0
- package/dist/common/backends/webgl/geodesicReconstruction.d.ts +13 -0
- package/dist/common/backends/webgl/geodesicReconstruction.js +176 -0
- package/dist/common/backends/webgl/jointBilateralFilter.d.ts +12 -0
- package/dist/common/backends/webgl/jointBilateralFilter.js +108 -0
- package/dist/common/backends/webgl/morphErode.d.ts +13 -0
- package/dist/common/backends/webgl/morphErode.js +121 -0
- package/dist/common/backends/webgl/renderer.js +194 -44
- package/dist/common/backends/webgl/replace.d.ts +17 -0
- package/dist/common/backends/webgl/replace.js +126 -0
- package/dist/common/backends/webgl/selectComponent.d.ts +15 -0
- package/dist/common/backends/webgl/selectComponent.js +102 -0
- package/dist/common/backends/webgl/smoothingMask.d.ts +3 -3
- package/dist/common/backends/webgl/smoothingMask.js +22 -21
- package/dist/common/backends/webgl/tentBlur.d.ts +9 -0
- package/dist/common/backends/webgl/tentBlur.js +118 -0
- package/dist/common/backends/webgl/texture.d.ts +2 -3
- package/dist/common/backends/webgl/texture.js +21 -19
- package/dist/common/backends/webgl/textureToTexture.d.ts +1 -1
- package/dist/common/backends/webgl/textureToTexture.js +19 -16
- package/dist/common/backends/webgl/webglUtils.d.ts +7 -1
- package/dist/common/backends/webgl/webglUtils.js +72 -10
- package/dist/common/backends/webgpu/blender.d.ts +6 -1
- package/dist/common/backends/webgpu/blender.js +96 -51
- package/dist/common/backends/webgpu/constants.js +1 -1
- package/dist/common/backends/webgpu/dualFilterBlur.d.ts +2 -3
- package/dist/common/backends/webgpu/dualFilterBlur.js +146 -140
- package/dist/common/backends/webgpu/jointBilateralFilter.d.ts +7 -0
- package/dist/common/backends/webgpu/jointBilateralFilter.js +162 -0
- package/dist/common/backends/webgpu/renderer.js +205 -43
- package/dist/common/backends/webgpu/replace.d.ts +11 -0
- package/dist/common/backends/webgpu/replace.js +175 -0
- package/dist/common/backends/webgpu/resize.d.ts +4 -0
- package/dist/common/backends/webgpu/resize.js +76 -0
- package/dist/common/backends/webgpu/smoothingMask.d.ts +2 -2
- package/dist/common/backends/webgpu/smoothingMask.js +74 -40
- package/dist/common/backends/webgpu/tentBlur.d.ts +5 -0
- package/dist/common/backends/webgpu/tentBlur.js +131 -0
- package/dist/common/backends/webgpu/texture.d.ts +2 -3
- package/dist/common/backends/webgpu/texture.js +29 -20
- package/dist/common/backends/webgpu/textureToTexture.d.ts +1 -1
- package/dist/common/backends/webgpu/textureToTexture.js +28 -15
- package/dist/common/backends/webgpu/types.d.ts +2 -0
- package/dist/common/backends/webgpu/webgpuUtils.d.ts +24 -4
- package/dist/common/backends/webgpu/webgpuUtils.js +60 -11
- package/dist/common/constants.d.ts +22 -5
- package/dist/common/constants.js +23 -5
- package/dist/common/index.d.ts +2 -0
- package/dist/common/index.js +2 -0
- package/dist/common/tsconfig.tsbuildinfo +1 -1
- package/dist/common/typeGuards.d.ts +2 -0
- package/dist/common/typeGuards.js +7 -0
- package/dist/common/types/media.d.ts +5 -1
- package/dist/common/types/messageEvents.d.ts +31 -4
- package/dist/common/types/processor.d.ts +6 -4
- package/dist/common/types/render.d.ts +147 -15
- package/dist/common/types/render.js +23 -1
- package/dist/common/types/segmentation.d.ts +0 -12
- package/dist/common/types/utils.d.ts +21 -0
- package/dist/common/utils.d.ts +29 -2
- package/dist/common/utils.js +206 -10
- package/dist/main/audio.js +3 -3
- package/dist/main/benchUtils.d.ts +35 -3
- package/dist/main/benchUtils.js +149 -15
- package/dist/main/index.d.ts +1 -1
- package/dist/main/index.js +1 -1
- package/dist/main/tsconfig.tsbuildinfo +1 -1
- package/dist/main/types.d.ts +0 -6
- package/dist/main/utils.d.ts +4 -0
- package/dist/main/utils.js +31 -0
- package/dist/main/video/constants.d.ts +1 -4
- package/dist/main/video/constants.js +1 -5
- package/dist/main/video/index.d.ts +3 -4
- package/dist/main/video/index.js +3 -4
- package/dist/main/video/inputProcessAdaptor.d.ts +30 -0
- package/dist/main/video/inputProcessAdaptor.js +198 -0
- package/dist/main/video/segmenter.d.ts +35 -0
- package/dist/main/video/{segmenters/mediapipe.js → segmenter.js} +93 -53
- package/dist/main/video/typeGuards.d.ts +2 -0
- package/dist/main/video/typeGuards.js +8 -1
- package/dist/main/video/types.d.ts +36 -16
- package/dist/main/video/utils.d.ts +5 -4
- package/dist/main/video/utils.js +49 -44
- package/dist/main/video/video.d.ts +29 -7
- package/dist/main/video/video.js +323 -28
- package/dist/workers/mediaWorker.js +35 -162
- package/dist/workers/tsconfig.tsbuildinfo +1 -1
- package/dist/worklets/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -12
- package/api-docs/functions/createCanvasTransform.mdx +0 -14
- package/api-docs/functions/createVideoTrackProcessor.mdx +0 -9
- package/api-docs/functions/createVideoTrackProcessorWithFallback.mdx +0 -15
- package/api-docs/interfaces/Detector.mdx +0 -81
- package/api-docs/interfaces/SegmentationParams.mdx +0 -18
- package/api-docs/interfaces/Transform.mdx +0 -62
- package/api-docs/type-aliases/ProcessVideoTrack.mdx +0 -15
- package/api-docs/type-aliases/SegmentationTransform.mdx +0 -3
- package/dist/common/backends/webgl/shaders/blendFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/blendFragmentShader.js +0 -21
- package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.js +0 -45
- package/dist/common/backends/webgl/shaders/smoothingMaskFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/smoothingMaskFragmentShader.js +0 -57
- package/dist/common/backends/webgl/shaders/textureFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/textureFragmentShader.js +0 -16
- package/dist/common/backends/webgl/shaders/vertexShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/vertexShader.js +0 -19
- package/dist/main/transformer.d.ts +0 -1
- package/dist/main/transformer.js +0 -4
- package/dist/main/video/canvasTransform.d.ts +0 -17
- package/dist/main/video/canvasTransform.js +0 -253
- package/dist/main/video/segmenters/mediapipe.d.ts +0 -22
- package/dist/main/video/transformer.d.ts +0 -9
- package/dist/main/video/transformer.js +0 -68
- package/dist/main/video/videoStreamTrackProcessor.d.ts +0 -20
- package/dist/main/video/videoStreamTrackProcessor.js +0 -86
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { assert } from '@pexip/utils';
|
|
2
|
-
import { PROCESS_STATUS, RENDERING_EVENTS } from '
|
|
3
|
-
|
|
4
|
-
const cloned = await createImageBitmap(image.image);
|
|
5
|
-
return { image: cloned, key: image.key };
|
|
6
|
-
};
|
|
2
|
+
import { PROCESS_STATUS, RENDERING_EVENTS } from '../../common/constants';
|
|
3
|
+
import { cloneImageRecord } from '../../common/utils';
|
|
7
4
|
export const createSegmenter = (
|
|
8
5
|
/**
|
|
9
6
|
* A base path to specify the directory the Wasm files should be loaded
|
|
@@ -12,7 +9,7 @@ export const createSegmenter = (
|
|
|
12
9
|
* @see {@link
|
|
13
10
|
* https://ai.google.dev/edge/api/mediapipe/js/tasks-vision.filesetresolver#filesetresolverforvisiontasks | FileResolver}
|
|
14
11
|
*/
|
|
15
|
-
basePath = '/', { workerScriptUrl = new URL('
|
|
12
|
+
basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', import.meta.url), credentials = 'same-origin', delegate, log, stats, ...options } = { modelAsset: { modelName: 'selfie', path: '' } }) => {
|
|
16
13
|
const props = {
|
|
17
14
|
status: PROCESS_STATUS.New,
|
|
18
15
|
modelAsset: options.modelAsset,
|
|
@@ -28,24 +25,35 @@ basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', i
|
|
|
28
25
|
error.preventDefault();
|
|
29
26
|
});
|
|
30
27
|
mediaWorker.addEventListener('message', (event) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (type === 'event') {
|
|
36
|
-
log?.(event.data.data.type, event.data.data.message);
|
|
37
|
-
for (const cb of props.eventHandlers.get(event.data.data.type) ?? []) {
|
|
38
|
-
cb();
|
|
28
|
+
switch (event.data.type) {
|
|
29
|
+
case 'debug': {
|
|
30
|
+
log?.(event.data.data.message, event.data.data.context);
|
|
31
|
+
break;
|
|
39
32
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
case 'event': {
|
|
34
|
+
log?.(event.data.data.type, event.data.data.message);
|
|
35
|
+
for (const cb of props.eventHandlers.get(event.data.data.type) ?? []) {
|
|
36
|
+
cb();
|
|
37
|
+
}
|
|
38
|
+
switch (event.data.data.type) {
|
|
39
|
+
case RENDERING_EVENTS.ProcessorRestarted:
|
|
40
|
+
break;
|
|
41
|
+
case RENDERING_EVENTS.ContextRestored:
|
|
42
|
+
void handleContextRestored();
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
props.status = PROCESS_STATUS.Error;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case 'stats': {
|
|
51
|
+
stats?.(event.data.data);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
default: {
|
|
55
|
+
// Do nothing
|
|
56
|
+
break;
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
});
|
|
@@ -72,7 +80,8 @@ basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', i
|
|
|
72
80
|
};
|
|
73
81
|
async function postMsg(expectedResponseType, message, transfer) {
|
|
74
82
|
if (!expectedResponseType) {
|
|
75
|
-
|
|
83
|
+
props.mediaWorker?.postMessage(message, transfer ?? []);
|
|
84
|
+
return;
|
|
76
85
|
}
|
|
77
86
|
return await new Promise((resolve, reject) => {
|
|
78
87
|
const handleMessage = (ev) => {
|
|
@@ -83,8 +92,10 @@ basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', i
|
|
|
83
92
|
resolve(ev.data.data);
|
|
84
93
|
break;
|
|
85
94
|
}
|
|
86
|
-
case 'debug':
|
|
87
|
-
|
|
95
|
+
case 'debug':
|
|
96
|
+
case 'stats': {
|
|
97
|
+
// Nothing to handle -- these are continues
|
|
98
|
+
// messages handled by the global event listener
|
|
88
99
|
break;
|
|
89
100
|
}
|
|
90
101
|
case 'event': {
|
|
@@ -113,17 +124,16 @@ basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', i
|
|
|
113
124
|
props.mediaWorker?.postMessage(message, transfer ?? []);
|
|
114
125
|
});
|
|
115
126
|
}
|
|
116
|
-
const open = async ({
|
|
127
|
+
const open = async ({ imageSegmenterOptions, ...restOptions }) => {
|
|
117
128
|
props.status = PROCESS_STATUS.Opening;
|
|
118
|
-
props.mediaWorker
|
|
119
|
-
|
|
129
|
+
if (!props.mediaWorker) {
|
|
130
|
+
props.mediaWorker = initWorker();
|
|
131
|
+
}
|
|
120
132
|
const backgroundImage = restOptions?.backgroundImage &&
|
|
121
133
|
(await cloneImageRecord(restOptions.backgroundImage));
|
|
122
134
|
try {
|
|
123
135
|
const processorSettings = {
|
|
124
136
|
basePath,
|
|
125
|
-
processingWidth,
|
|
126
|
-
processingHeight,
|
|
127
137
|
...restOptions,
|
|
128
138
|
imageSegmenterOptions: {
|
|
129
139
|
...options,
|
|
@@ -133,43 +143,66 @@ basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', i
|
|
|
133
143
|
backgroundImage,
|
|
134
144
|
};
|
|
135
145
|
log?.('Init processor with config', { config: processorSettings });
|
|
136
|
-
await postMsg('opened', {
|
|
146
|
+
const result = await postMsg('opened', {
|
|
137
147
|
type: 'open',
|
|
138
148
|
data: processorSettings,
|
|
139
|
-
},
|
|
149
|
+
}, backgroundImage?.image ? [backgroundImage.image] : []);
|
|
140
150
|
props.status = PROCESS_STATUS.Opened;
|
|
151
|
+
return result;
|
|
141
152
|
}
|
|
142
153
|
catch (error) {
|
|
143
154
|
props.status = PROCESS_STATUS.Error;
|
|
144
155
|
log?.('Failed to open process', {
|
|
145
156
|
error: error,
|
|
146
157
|
});
|
|
158
|
+
return {};
|
|
147
159
|
}
|
|
148
160
|
};
|
|
149
|
-
|
|
161
|
+
async function process(input, options) {
|
|
150
162
|
if (props.status === PROCESS_STATUS.Error ||
|
|
151
163
|
props.status === PROCESS_STATUS.Updating) {
|
|
152
164
|
return input;
|
|
153
165
|
}
|
|
154
166
|
props.status = PROCESS_STATUS.Processing;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
167
|
+
if (input instanceof MediaStreamTrack) {
|
|
168
|
+
try {
|
|
169
|
+
// Clone the track and keep the original in main
|
|
170
|
+
const clonedTrack = input.clone();
|
|
171
|
+
const output = await postMsg('processed', {
|
|
172
|
+
type: 'process',
|
|
173
|
+
data: { track: clonedTrack, renderOptions: options },
|
|
174
|
+
}, [clonedTrack]);
|
|
175
|
+
assert(output);
|
|
161
176
|
props.status = PROCESS_STATUS.Idle;
|
|
177
|
+
return output;
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
props.status = PROCESS_STATUS.Error;
|
|
181
|
+
log?.('Error during the processing, return original track instead.', { error });
|
|
182
|
+
return input;
|
|
162
183
|
}
|
|
163
|
-
return resultFrame;
|
|
164
184
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
185
|
+
else {
|
|
186
|
+
try {
|
|
187
|
+
await postMsg('processed', {
|
|
188
|
+
type: 'process',
|
|
189
|
+
data: {
|
|
190
|
+
readable: input.readable,
|
|
191
|
+
writable: input.writable,
|
|
192
|
+
renderOptions: options,
|
|
193
|
+
},
|
|
194
|
+
}, [input.readable, input.writable]);
|
|
195
|
+
props.status = PROCESS_STATUS.Idle;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
props.status = PROCESS_STATUS.Error;
|
|
199
|
+
log?.('Error during the processing, return original ReadWritableStream instead.', {
|
|
200
|
+
error,
|
|
201
|
+
});
|
|
202
|
+
return input;
|
|
203
|
+
}
|
|
171
204
|
}
|
|
172
|
-
}
|
|
205
|
+
}
|
|
173
206
|
const update = async (options) => {
|
|
174
207
|
try {
|
|
175
208
|
if (props.status === PROCESS_STATUS.Processing ||
|
|
@@ -183,7 +216,8 @@ basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', i
|
|
|
183
216
|
props.mediaWorker?.removeEventListener('message', handleMessage);
|
|
184
217
|
resolve(undefined);
|
|
185
218
|
}
|
|
186
|
-
else
|
|
219
|
+
else if (ev.data.type !== 'debug' &&
|
|
220
|
+
ev.data.type !== 'stats') {
|
|
187
221
|
reject(new Error(`UnhandledEvent: ${ev.data.type}`));
|
|
188
222
|
}
|
|
189
223
|
};
|
|
@@ -196,7 +230,7 @@ basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', i
|
|
|
196
230
|
(await cloneImageRecord(options.backgroundImage));
|
|
197
231
|
log?.('Update processor config', { config: options });
|
|
198
232
|
const transfer = backgroundImage ? [backgroundImage.image] : [];
|
|
199
|
-
await postMsg('updated', {
|
|
233
|
+
const changed = await postMsg('updated', {
|
|
200
234
|
type: 'update',
|
|
201
235
|
data: {
|
|
202
236
|
...options,
|
|
@@ -204,25 +238,30 @@ basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', i
|
|
|
204
238
|
},
|
|
205
239
|
}, transfer);
|
|
206
240
|
props.status = PROCESS_STATUS.Updated;
|
|
241
|
+
return changed;
|
|
207
242
|
}
|
|
208
243
|
catch (error) {
|
|
209
244
|
props.status = PROCESS_STATUS.Error;
|
|
210
245
|
log?.('Failed to update process', {
|
|
211
246
|
error: error,
|
|
212
247
|
});
|
|
248
|
+
return {};
|
|
213
249
|
}
|
|
214
250
|
};
|
|
251
|
+
const mute = (muted) => {
|
|
252
|
+
postMsg(undefined, { type: 'mute', data: muted });
|
|
253
|
+
};
|
|
215
254
|
const close = () => {
|
|
216
255
|
props.status = PROCESS_STATUS.Closing;
|
|
217
256
|
log?.('Close processor');
|
|
218
|
-
|
|
219
|
-
props.eventHandlers.clear();
|
|
257
|
+
postMsg(undefined, { type: 'close' });
|
|
220
258
|
props.status = PROCESS_STATUS.Closed;
|
|
221
259
|
};
|
|
222
260
|
const destroy = async () => {
|
|
223
261
|
props.status = PROCESS_STATUS.Destroying;
|
|
224
262
|
log?.('Destroy processor');
|
|
225
|
-
|
|
263
|
+
props.eventHandlers.clear();
|
|
264
|
+
postMsg(undefined, { type: 'destroy' });
|
|
226
265
|
props.mediaWorker?.terminate();
|
|
227
266
|
props.mediaWorker = undefined;
|
|
228
267
|
props.status = PROCESS_STATUS.Destroyed;
|
|
@@ -251,6 +290,7 @@ basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', i
|
|
|
251
290
|
open,
|
|
252
291
|
update,
|
|
253
292
|
process,
|
|
293
|
+
mute,
|
|
254
294
|
close,
|
|
255
295
|
destroy,
|
|
256
296
|
};
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import { type RenderEffects } from '../../common/constants';
|
|
2
2
|
export declare const isRenderEffects: (t: unknown) => t is RenderEffects;
|
|
3
|
+
export declare const isMediaStreamVideoTrack: (t: MediaStreamTrack) => t is MediaStreamVideoTrack;
|
|
4
|
+
export declare function assertMediaStreamVideoTrack(t: MediaStreamTrack): asserts t is MediaStreamVideoTrack;
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { RENDER_EFFECT } from '../../common/constants';
|
|
2
|
+
const EFFECTS = Object.values(RENDER_EFFECT);
|
|
2
3
|
export const isRenderEffects = (t) => {
|
|
3
4
|
if (typeof t !== 'string') {
|
|
4
5
|
return false;
|
|
5
6
|
}
|
|
6
|
-
return
|
|
7
|
+
return EFFECTS.some(effect => effect === t);
|
|
7
8
|
};
|
|
9
|
+
export const isMediaStreamVideoTrack = (t) => t.kind === 'video';
|
|
10
|
+
export function assertMediaStreamVideoTrack(t) {
|
|
11
|
+
if (!isMediaStreamVideoTrack(t)) {
|
|
12
|
+
throw new TypeError('Not a video track', { cause: t });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { ProcessStatus, RenderingEvents } from '../../common/constants';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ReadWritableStream } from '../../common/types/media';
|
|
3
3
|
import type { RendererOptions } from '../../common/types/render';
|
|
4
4
|
import type { SegmentationModelAsset } from '../../common/types/segmentation';
|
|
5
5
|
import type { ProcessorOptions } from '../../common/types/processor';
|
|
6
|
-
import type { Transform } from '../types';
|
|
7
6
|
export type Color = {
|
|
8
7
|
r: number;
|
|
9
8
|
g: number;
|
|
@@ -16,9 +15,9 @@ export interface AsyncAssets {
|
|
|
16
15
|
tfjsBackendLoaded: boolean;
|
|
17
16
|
glueLoaded: boolean;
|
|
18
17
|
}
|
|
19
|
-
export interface Process {
|
|
18
|
+
export interface Process<T> {
|
|
20
19
|
status: ProcessStatus;
|
|
21
|
-
open(): Promise<
|
|
20
|
+
open(): Promise<T>;
|
|
22
21
|
close(): void;
|
|
23
22
|
destroy(): Promise<void>;
|
|
24
23
|
}
|
|
@@ -28,21 +27,42 @@ export interface BackgroundImage {
|
|
|
28
27
|
}
|
|
29
28
|
type Unsubscribe = () => void;
|
|
30
29
|
type Callback<T> = (args: T) => void;
|
|
31
|
-
export interface Segmenter extends Process {
|
|
30
|
+
export interface Segmenter extends Process<Partial<RendererOptions>> {
|
|
32
31
|
modelAsset: SegmentationModelAsset;
|
|
33
32
|
backgroundImage?: BackgroundImage;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Initialize the Segmenter as well as the underlying worker.
|
|
35
|
+
* It is expected to be called once.
|
|
36
|
+
*/
|
|
37
|
+
open(options?: ProcessorOptions): Promise<Partial<RendererOptions>>;
|
|
38
|
+
/**
|
|
39
|
+
* Update the processor options
|
|
40
|
+
*/
|
|
41
|
+
update(options: ProcessorOptions): Promise<Partial<RendererOptions>>;
|
|
42
|
+
/**
|
|
43
|
+
* Mute/unmute the track in the worker
|
|
44
|
+
*/
|
|
45
|
+
mute(muted: boolean): void;
|
|
46
|
+
/**
|
|
47
|
+
* Process provided input
|
|
48
|
+
*/
|
|
49
|
+
process(input: MediaStreamTrack, options?: RendererOptions): Promise<MediaStreamTrack>;
|
|
50
|
+
process(input: ReadWritableStream<VideoFrame>, options?: RendererOptions): Promise<ReadWritableStream<VideoFrame> | undefined>;
|
|
51
|
+
process(input: MediaStreamTrack | ReadWritableStream<VideoFrame>, options?: RendererOptions): Promise<MediaStreamTrack | ReadWritableStream<VideoFrame> | undefined>;
|
|
52
|
+
/**
|
|
53
|
+
* Subscribe rendering events, @see RenderingEvents
|
|
54
|
+
*/
|
|
37
55
|
subscribe(event: RenderingEvents, callback: Callback<void>): Unsubscribe;
|
|
38
56
|
}
|
|
39
|
-
export interface
|
|
40
|
-
|
|
57
|
+
export interface InputProcessAdaptor {
|
|
58
|
+
/**
|
|
59
|
+
* Adapt the provided input track based on browser capabilities.
|
|
60
|
+
*/
|
|
61
|
+
adapt(track: MediaStreamTrack): MediaStreamTrack | ReadWritableStream<VideoFrame>;
|
|
62
|
+
/**
|
|
63
|
+
* The generated track from the `adapt` function, and only available
|
|
64
|
+
* after calling the `adapt` method otherwise `undefined`
|
|
65
|
+
*/
|
|
66
|
+
outputTrack?: MediaStreamTrack;
|
|
41
67
|
}
|
|
42
|
-
export interface SegmentationParams extends RendererOptions {
|
|
43
|
-
segmenter: Segmenter;
|
|
44
|
-
width: number;
|
|
45
|
-
height: number;
|
|
46
|
-
}
|
|
47
|
-
export type SegmentationTransform = Transform<InputFrame, InputFrame> & SegmentationParams & Pick<Segmenter, 'update'> & Omit<Process, 'open'>;
|
|
48
68
|
export {};
|
|
@@ -58,13 +58,14 @@ export declare const toVideoElement: (input: MediaStream, width: number, height:
|
|
|
58
58
|
export declare const playVideo: (video: HTMLVideoElement, { setTimeout, clearTimeout }?: {
|
|
59
59
|
setTimeout?: (((handler: TimerHandler, timeout?: number, ...arguments: any[]) => number) & typeof globalThis.setTimeout) | undefined;
|
|
60
60
|
clearTimeout?: (((id: number | undefined) => void) & typeof globalThis.clearTimeout) | undefined;
|
|
61
|
-
}) => Promise<void
|
|
61
|
+
}) => Promise<[void, void]> | undefined;
|
|
62
62
|
/**
|
|
63
63
|
* Load image with provided image element
|
|
64
64
|
*/
|
|
65
65
|
export declare const loadImage: (image: HTMLImageElement, src: string) => Promise<void>;
|
|
66
66
|
export declare const toNumber: (value: number | SVGAnimatedLength) => number;
|
|
67
|
-
export declare
|
|
67
|
+
export declare function getCanvasRenderingContext2D(canvas: HTMLCanvasElement, options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D;
|
|
68
|
+
export declare function getCanvasRenderingContext2D(canvas: OffscreenCanvas, options?: CanvasRenderingContext2DSettings): OffscreenCanvasRenderingContext2D;
|
|
68
69
|
export declare const hasVideoFrameSupport: () => boolean;
|
|
69
70
|
export declare const isVideoFrame: (t: unknown) => t is VideoFrame;
|
|
70
71
|
export declare const flipCanvasHorizontal: (canvas: Canvas) => void;
|
|
@@ -89,8 +90,8 @@ interface FrameCallbackRequestOptions {
|
|
|
89
90
|
* @param frameRate - A fallback frame rate when we are not able to get the rate
|
|
90
91
|
* from API
|
|
91
92
|
*/
|
|
92
|
-
export declare const createFrameCallbackRequest: (callback: Callback<Promise<void>, [
|
|
93
|
-
start: (input:
|
|
93
|
+
export declare const createFrameCallbackRequest: <T>(callback: Callback<Promise<void>, [T]>, frameRate: number, { subscribeVisibilityChange, }?: FrameCallbackRequestOptions) => {
|
|
94
|
+
start: (input: T) => Promise<void>;
|
|
94
95
|
stop: () => void;
|
|
95
96
|
frameRate: number;
|
|
96
97
|
};
|
package/dist/main/video/utils.js
CHANGED
|
@@ -126,52 +126,57 @@ var HTMLMediaElementReadyState;
|
|
|
126
126
|
HTMLMediaElementReadyState[HTMLMediaElementReadyState["HaveFutureData"] = 3] = "HaveFutureData";
|
|
127
127
|
HTMLMediaElementReadyState[HTMLMediaElementReadyState["HaveEnoughData"] = 4] = "HaveEnoughData";
|
|
128
128
|
})(HTMLMediaElementReadyState || (HTMLMediaElementReadyState = {}));
|
|
129
|
-
export const playVideo =
|
|
129
|
+
export const playVideo = (video, { setTimeout = window.setTimeout, clearTimeout = window.clearTimeout } = {}) => {
|
|
130
130
|
if (video.autoplay) {
|
|
131
131
|
return;
|
|
132
132
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
133
|
+
return Promise.all([
|
|
134
|
+
video.play(),
|
|
135
|
+
new Promise((resolve, reject) => {
|
|
136
|
+
if (video.readyState <
|
|
137
|
+
HTMLMediaElementReadyState.HaveCurrentData) {
|
|
138
|
+
// Not enough data, wait for the data and then play
|
|
139
|
+
const clearTimeoutTimer = () => {
|
|
140
|
+
clearTimeout(timeoutId);
|
|
141
|
+
timeoutId = 0;
|
|
142
|
+
};
|
|
143
|
+
const unsubscribe = () => {
|
|
144
|
+
video.removeEventListener('loadeddata', waitForEvent);
|
|
145
|
+
video.removeEventListener('error', handleError);
|
|
146
|
+
video.removeEventListener('stalled', handleStalled);
|
|
147
|
+
video.removeEventListener('ended', handleEnded);
|
|
148
|
+
clearTimeoutTimer();
|
|
149
|
+
};
|
|
150
|
+
const handleEnded = () => {
|
|
151
|
+
unsubscribe();
|
|
152
|
+
reject(new Error('VideoEnded', { cause: video }));
|
|
153
|
+
};
|
|
154
|
+
const handleStalled = () => {
|
|
155
|
+
unsubscribe();
|
|
156
|
+
reject(new Error('VideoStalled', { cause: video }));
|
|
157
|
+
};
|
|
158
|
+
const handleError = (error) => {
|
|
159
|
+
unsubscribe();
|
|
160
|
+
reject(error.error);
|
|
161
|
+
};
|
|
162
|
+
const waitForEvent = () => {
|
|
163
|
+
unsubscribe();
|
|
164
|
+
resolve();
|
|
165
|
+
};
|
|
166
|
+
// Use setTimeout to prevent a race to `loadeddata` event
|
|
167
|
+
video.addEventListener('loadeddata', waitForEvent);
|
|
168
|
+
video.addEventListener('error', handleError);
|
|
169
|
+
video.addEventListener('stalled', handleStalled);
|
|
170
|
+
video.addEventListener('ended', handleEnded);
|
|
171
|
+
let timeoutId = setTimeout(() => {
|
|
172
|
+
reject(new Error('PlayVideoTimeout', { cause: video }));
|
|
173
|
+
}, PLAY_VIDEO_TIMEOUT);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
162
176
|
resolve();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
video.addEventListener('error', handleError);
|
|
167
|
-
video.addEventListener('stalled', handleStalled);
|
|
168
|
-
video.addEventListener('ended', handleEnded);
|
|
169
|
-
let timeoutId = setTimeout(() => {
|
|
170
|
-
reject(new Error('PlayVideoTimeout', { cause: video }));
|
|
171
|
-
}, PLAY_VIDEO_TIMEOUT);
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
return await video.play();
|
|
177
|
+
}
|
|
178
|
+
}),
|
|
179
|
+
]);
|
|
175
180
|
};
|
|
176
181
|
/**
|
|
177
182
|
* Load image with provided image element
|
|
@@ -184,13 +189,13 @@ export const loadImage = async (image, src) => {
|
|
|
184
189
|
});
|
|
185
190
|
};
|
|
186
191
|
export const toNumber = (value) => value instanceof SVGAnimatedLength ? value.baseVal.value : value;
|
|
187
|
-
export
|
|
192
|
+
export function getCanvasRenderingContext2D(canvas, options) {
|
|
188
193
|
const context = canvas.getContext('2d', options);
|
|
189
194
|
if (!context) {
|
|
190
195
|
throw new Error('Cannot get CanvasRenderingContext2D');
|
|
191
196
|
}
|
|
192
197
|
return context;
|
|
193
|
-
}
|
|
198
|
+
}
|
|
194
199
|
export const hasVideoFrameSupport = () => 'VideoFrame' in window;
|
|
195
200
|
export const isVideoFrame = (t) => {
|
|
196
201
|
if (hasVideoFrameSupport() && t instanceof VideoFrame) {
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { ProcessorOptions, ProcessStatus, RendererOptions } from '../../common';
|
|
2
|
+
import type { Segmenter, InputProcessAdaptor } from './types';
|
|
3
|
+
export interface ProcessorUpdateOptions extends Omit<ProcessorOptions, 'renderOptions'> {
|
|
4
|
+
renderOptions?: Partial<RendererOptions>;
|
|
5
|
+
}
|
|
5
6
|
export interface VideoProcessor {
|
|
6
|
-
|
|
7
|
+
readonly status: ProcessStatus;
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the video processor and related
|
|
10
|
+
*/
|
|
11
|
+
open(options?: ProcessorUpdateOptions): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Update the video processor with provided options
|
|
14
|
+
*/
|
|
15
|
+
update(options: ProcessorUpdateOptions): Promise<void>;
|
|
16
|
+
mute(muted: boolean): void;
|
|
7
17
|
/**
|
|
8
18
|
* Process the video and return a MediaStream
|
|
9
19
|
*/
|
|
10
|
-
process: (source:
|
|
20
|
+
process: (source: MediaStreamTrack) => Promise<MediaStreamTrack>;
|
|
11
21
|
/**
|
|
12
22
|
* Stop the process of video segmentation
|
|
13
23
|
*/
|
|
@@ -16,5 +26,17 @@ export interface VideoProcessor {
|
|
|
16
26
|
* Destroy the segmentation wasm instance
|
|
17
27
|
*/
|
|
18
28
|
destroy: () => Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Current renderer options
|
|
31
|
+
*/
|
|
32
|
+
readonly renderOptions: RendererOptions;
|
|
33
|
+
/**
|
|
34
|
+
* Current processing width
|
|
35
|
+
*/
|
|
36
|
+
readonly processingWidth: number;
|
|
37
|
+
/**
|
|
38
|
+
* Current processing height
|
|
39
|
+
*/
|
|
40
|
+
readonly processingHeight: number;
|
|
19
41
|
}
|
|
20
|
-
export declare const createVideoProcessor: (
|
|
42
|
+
export declare const createVideoProcessor: (segmenter: Segmenter, inputProcessAdaptor: InputProcessAdaptor) => VideoProcessor;
|