@pexip/media-processor 19.0.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 +36 -0
- package/dist/common/backends/webgl/blur.js +7 -5
- package/dist/common/backends/webgl/renderer.d.ts +1 -1
- package/dist/common/backends/webgl/renderer.js +27 -34
- package/dist/common/backends/webgl/smoothingMask.d.ts +1 -1
- package/dist/common/backends/webgl/webglUtils.d.ts +1 -1
- package/dist/common/backends/webgl/webglUtils.js +9 -10
- package/dist/common/backends/webgpu/blender.d.ts +8 -0
- package/dist/common/backends/webgpu/blender.js +78 -0
- package/dist/common/backends/webgpu/constants.d.ts +1 -0
- package/dist/common/backends/webgpu/constants.js +1 -0
- package/dist/common/backends/webgpu/dualFilterBlur.d.ts +5 -0
- package/dist/common/backends/webgpu/dualFilterBlur.js +201 -0
- package/dist/common/backends/webgpu/renderer.d.ts +2 -0
- package/dist/common/backends/webgpu/renderer.js +127 -0
- package/dist/common/backends/webgpu/smoothingMask.d.ts +5 -0
- package/dist/common/backends/webgpu/smoothingMask.js +66 -0
- package/dist/common/backends/webgpu/texture.d.ts +5 -0
- package/dist/common/backends/webgpu/texture.js +35 -0
- package/dist/common/backends/webgpu/textureToTexture.d.ts +4 -0
- package/dist/common/backends/webgpu/textureToTexture.js +35 -0
- package/dist/common/backends/webgpu/types.d.ts +5 -0
- package/dist/common/backends/webgpu/types.js +1 -0
- package/dist/common/backends/webgpu/webgpuUtils.d.ts +7 -0
- package/dist/common/backends/webgpu/webgpuUtils.js +14 -0
- package/dist/common/canvasRenderUtils.d.ts +1 -1
- package/dist/common/canvasRenderUtils.js +8 -10
- package/dist/common/constants.d.ts +5 -0
- package/dist/common/constants.js +4 -0
- package/dist/common/index.d.ts +0 -1
- package/dist/common/index.js +0 -1
- package/dist/common/m3.d.ts +14 -8
- package/dist/common/m3.js +0 -1
- 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/render.d.ts +7 -4
- package/dist/common/types/segmentation.d.ts +5 -1
- package/dist/common/utils.d.ts +4 -1
- package/dist/common/utils.js +36 -1
- package/dist/main/audio.d.ts +1 -1
- package/dist/main/audio.js +7 -5
- package/dist/main/math.js +1 -1
- package/dist/main/process.d.ts +3 -3
- package/dist/main/process.js +2 -2
- package/dist/main/processor.d.ts +0 -1
- package/dist/main/transformer.d.ts +1 -1
- package/dist/main/tsconfig.tsbuildinfo +1 -1
- package/dist/main/typeGuards.d.ts +1 -1
- package/dist/main/types.d.ts +2 -1
- package/dist/main/utils.js +6 -6
- package/dist/main/video/canvasTransform.d.ts +1 -1
- package/dist/main/video/canvasTransform.js +66 -12
- package/dist/main/video/segmenters/mediapipe.d.ts +1 -11
- package/dist/main/video/segmenters/mediapipe.js +37 -22
- package/dist/main/video/transformer.d.ts +1 -1
- package/dist/main/video/typeGuards.d.ts +2 -1
- package/dist/main/video/types.d.ts +2 -12
- package/dist/main/video/utils.d.ts +2 -2
- package/dist/main/video/utils.js +2 -2
- package/dist/main/video/video.d.ts +3 -2
- package/dist/main/video/video.js +5 -3
- package/dist/main/video/videoStreamTrackProcessor.d.ts +1 -1
- package/dist/main/video/videoStreamTrackProcessor.js +2 -2
- package/dist/workers/mediaWorker.js +18 -18
- package/dist/workers/tsconfig.tsbuildinfo +1 -1
- package/dist/worklets/tsconfig.tsbuildinfo +1 -1
- package/dist/worklets/types.d.ts +0 -2
- package/package.json +45 -9
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
import type { Segmenter } from '../types';
|
|
2
2
|
import type { Delegate, SegmenterOptions, ImageSegmenterOptions, SegmentationModelAsset } from '../../../common/types/segmentation';
|
|
3
3
|
interface Options extends Omit<ImageSegmenterOptions, 'delegate'> {
|
|
4
|
-
/**
|
|
5
|
-
* Processing width for the processing canvas.
|
|
6
|
-
* @defaultValue 768
|
|
7
|
-
*/
|
|
8
|
-
processingWidth?: number;
|
|
9
|
-
/**
|
|
10
|
-
* Processing height for the processing canvas.
|
|
11
|
-
* @defaultValue 432
|
|
12
|
-
*/
|
|
13
|
-
processingHeight?: number;
|
|
14
4
|
/**
|
|
15
5
|
* The URL for the worker script.
|
|
16
6
|
*/
|
|
@@ -28,5 +18,5 @@ interface Options extends Omit<ImageSegmenterOptions, 'delegate'> {
|
|
|
28
18
|
delegate?: () => Delegate;
|
|
29
19
|
log?: (message: string, meta?: unknown) => void;
|
|
30
20
|
}
|
|
31
|
-
export declare const createSegmenter: (basePath
|
|
21
|
+
export declare const createSegmenter: (basePath?: SegmenterOptions["basePath"], { workerScriptUrl, credentials, delegate, log, ...options }?: Options) => Segmenter;
|
|
32
22
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assert } from '@pexip/utils';
|
|
2
|
-
import { PROCESS_STATUS,
|
|
2
|
+
import { PROCESS_STATUS, RENDERING_EVENTS } from '../../../common/constants';
|
|
3
3
|
const cloneImageRecord = async (image) => {
|
|
4
4
|
const cloned = await createImageBitmap(image.image);
|
|
5
5
|
return { image: cloned, key: image.key };
|
|
@@ -12,7 +12,7 @@ export const createSegmenter = (
|
|
|
12
12
|
* @see {@link
|
|
13
13
|
* https://ai.google.dev/edge/api/mediapipe/js/tasks-vision.filesetresolver#filesetresolverforvisiontasks | FileResolver}
|
|
14
14
|
*/
|
|
15
|
-
basePath = '/', {
|
|
15
|
+
basePath = '/', { workerScriptUrl = new URL('../../../workers/mediaWorker.js', import.meta.url), credentials = 'same-origin', delegate, log, ...options } = { modelAsset: { modelName: 'selfie', path: '' } }) => {
|
|
16
16
|
const props = {
|
|
17
17
|
status: PROCESS_STATUS.New,
|
|
18
18
|
modelAsset: options.modelAsset,
|
|
@@ -34,11 +34,9 @@ basePath = '/', { processingWidth = PROCESSING_WIDTH, processingHeight = PROCESS
|
|
|
34
34
|
}
|
|
35
35
|
if (type === 'event') {
|
|
36
36
|
log?.(event.data.data.type, event.data.data.message);
|
|
37
|
-
props.eventHandlers
|
|
38
|
-
.get(event.data.data.type)
|
|
39
|
-
?.forEach(cb => {
|
|
37
|
+
for (const cb of props.eventHandlers.get(event.data.data.type) ?? []) {
|
|
40
38
|
cb();
|
|
41
|
-
}
|
|
39
|
+
}
|
|
42
40
|
switch (event.data.data.type) {
|
|
43
41
|
case RENDERING_EVENTS.ProcessorRestarted:
|
|
44
42
|
break;
|
|
@@ -55,14 +53,15 @@ basePath = '/', { processingWidth = PROCESSING_WIDTH, processingHeight = PROCESS
|
|
|
55
53
|
};
|
|
56
54
|
const handleContextRestored = async () => {
|
|
57
55
|
try {
|
|
56
|
+
props.status = PROCESS_STATUS.Updating;
|
|
58
57
|
await postMsg('updated', {
|
|
59
58
|
type: 'update',
|
|
60
59
|
data: { restart: true },
|
|
61
60
|
});
|
|
62
61
|
props.status = PROCESS_STATUS.Updated;
|
|
63
|
-
props.eventHandlers.get('
|
|
62
|
+
for (const cb of props.eventHandlers.get('ContextRestored') ?? []) {
|
|
64
63
|
cb();
|
|
65
|
-
}
|
|
64
|
+
}
|
|
66
65
|
}
|
|
67
66
|
catch (err) {
|
|
68
67
|
log?.('Failed to restart process', {
|
|
@@ -114,22 +113,22 @@ basePath = '/', { processingWidth = PROCESSING_WIDTH, processingHeight = PROCESS
|
|
|
114
113
|
props.mediaWorker?.postMessage(message, transfer ?? []);
|
|
115
114
|
});
|
|
116
115
|
}
|
|
117
|
-
const open = async (
|
|
116
|
+
const open = async ({ processingWidth, processingHeight, output, imageSegmenterOptions, ...restOptions }) => {
|
|
118
117
|
props.status = PROCESS_STATUS.Opening;
|
|
119
118
|
props.mediaWorker = initWorker();
|
|
120
|
-
props.output =
|
|
121
|
-
const backgroundImage =
|
|
122
|
-
(await cloneImageRecord(
|
|
119
|
+
props.output = output;
|
|
120
|
+
const backgroundImage = restOptions?.backgroundImage &&
|
|
121
|
+
(await cloneImageRecord(restOptions.backgroundImage));
|
|
123
122
|
try {
|
|
124
123
|
const processorSettings = {
|
|
125
124
|
basePath,
|
|
126
125
|
processingWidth,
|
|
127
126
|
processingHeight,
|
|
128
|
-
...
|
|
127
|
+
...restOptions,
|
|
129
128
|
imageSegmenterOptions: {
|
|
130
129
|
...options,
|
|
131
130
|
delegate: delegate?.(),
|
|
132
|
-
...
|
|
131
|
+
...imageSegmenterOptions,
|
|
133
132
|
},
|
|
134
133
|
backgroundImage,
|
|
135
134
|
};
|
|
@@ -137,7 +136,7 @@ basePath = '/', { processingWidth = PROCESSING_WIDTH, processingHeight = PROCESS
|
|
|
137
136
|
await postMsg('opened', {
|
|
138
137
|
type: 'open',
|
|
139
138
|
data: processorSettings,
|
|
140
|
-
}, [
|
|
139
|
+
}, [output, backgroundImage?.image].filter(Boolean));
|
|
141
140
|
props.status = PROCESS_STATUS.Opened;
|
|
142
141
|
}
|
|
143
142
|
catch (error) {
|
|
@@ -148,7 +147,8 @@ basePath = '/', { processingWidth = PROCESSING_WIDTH, processingHeight = PROCESS
|
|
|
148
147
|
}
|
|
149
148
|
};
|
|
150
149
|
const process = async (input, options) => {
|
|
151
|
-
if (props.status === PROCESS_STATUS.Error
|
|
150
|
+
if (props.status === PROCESS_STATUS.Error ||
|
|
151
|
+
props.status === PROCESS_STATUS.Updating) {
|
|
152
152
|
return input;
|
|
153
153
|
}
|
|
154
154
|
props.status = PROCESS_STATUS.Processing;
|
|
@@ -172,6 +172,26 @@ basePath = '/', { processingWidth = PROCESSING_WIDTH, processingHeight = PROCESS
|
|
|
172
172
|
};
|
|
173
173
|
const update = async (options) => {
|
|
174
174
|
try {
|
|
175
|
+
if (props.status === PROCESS_STATUS.Processing ||
|
|
176
|
+
props.status === PROCESS_STATUS.Opening ||
|
|
177
|
+
props.status === PROCESS_STATUS.Updating) {
|
|
178
|
+
const waitForStatus = new Promise((resolve, reject) => {
|
|
179
|
+
const handleMessage = (ev) => {
|
|
180
|
+
if (ev.data.type === 'processed' ||
|
|
181
|
+
ev.data.type === 'opened' ||
|
|
182
|
+
ev.data.type === 'updated') {
|
|
183
|
+
props.mediaWorker?.removeEventListener('message', handleMessage);
|
|
184
|
+
resolve(undefined);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
reject(new Error(`UnhandledEvent: ${ev.data.type}`));
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
props.mediaWorker?.addEventListener('message', handleMessage);
|
|
191
|
+
});
|
|
192
|
+
await waitForStatus;
|
|
193
|
+
}
|
|
194
|
+
props.status = PROCESS_STATUS.Updating;
|
|
175
195
|
const backgroundImage = options.backgroundImage &&
|
|
176
196
|
(await cloneImageRecord(options.backgroundImage));
|
|
177
197
|
log?.('Update processor config', { config: options });
|
|
@@ -183,6 +203,7 @@ basePath = '/', { processingWidth = PROCESSING_WIDTH, processingHeight = PROCESS
|
|
|
183
203
|
backgroundImage,
|
|
184
204
|
},
|
|
185
205
|
}, transfer);
|
|
206
|
+
props.status = PROCESS_STATUS.Updated;
|
|
186
207
|
}
|
|
187
208
|
catch (error) {
|
|
188
209
|
props.status = PROCESS_STATUS.Error;
|
|
@@ -217,12 +238,6 @@ basePath = '/', { processingWidth = PROCESSING_WIDTH, processingHeight = PROCESS
|
|
|
217
238
|
get status() {
|
|
218
239
|
return props.status;
|
|
219
240
|
},
|
|
220
|
-
get width() {
|
|
221
|
-
return processingWidth;
|
|
222
|
-
},
|
|
223
|
-
get height() {
|
|
224
|
-
return processingHeight;
|
|
225
|
-
},
|
|
226
241
|
subscribe: (event, callback) => {
|
|
227
242
|
let handlers = props.eventHandlers.get(event);
|
|
228
243
|
if (!handlers) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { InputFrame } from './types';
|
|
2
|
-
export declare const nullTransformController: (controller: Pick<TransformStreamDefaultController<CanvasImageSource>,
|
|
2
|
+
export declare const nullTransformController: (controller: Pick<TransformStreamDefaultController<CanvasImageSource>, "enqueue" | "terminate">) => TransformStreamDefaultController<CanvasImageSource>;
|
|
3
3
|
export declare const adaptInputFrameTransformer: (transformer: Transformer<InputFrame, InputFrame>) => {
|
|
4
4
|
transform: (frame: VideoFrame, controller: TransformStreamDefaultController<VideoFrame>) => void | PromiseLike<void> | undefined;
|
|
5
5
|
flush?: TransformerFlushCallback<InputFrame> | undefined;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { type RenderEffects } from '../../common/constants';
|
|
2
|
+
export declare const isRenderEffects: (t: unknown) => t is RenderEffects;
|
|
@@ -11,16 +11,6 @@ export type Color = {
|
|
|
11
11
|
a: number;
|
|
12
12
|
};
|
|
13
13
|
export type InputFrame = HTMLCanvasElement | OffscreenCanvas | VideoFrame | ImageBitmap;
|
|
14
|
-
interface ProcessingSize {
|
|
15
|
-
/**
|
|
16
|
-
* Processing Width size
|
|
17
|
-
*/
|
|
18
|
-
width: number;
|
|
19
|
-
/**
|
|
20
|
-
* Processing height size
|
|
21
|
-
*/
|
|
22
|
-
height: number;
|
|
23
|
-
}
|
|
24
14
|
export interface AsyncAssets {
|
|
25
15
|
tfjsCoreLoaded: boolean;
|
|
26
16
|
tfjsBackendLoaded: boolean;
|
|
@@ -38,7 +28,7 @@ export interface BackgroundImage {
|
|
|
38
28
|
}
|
|
39
29
|
type Unsubscribe = () => void;
|
|
40
30
|
type Callback<T> = (args: T) => void;
|
|
41
|
-
export interface Segmenter extends Process
|
|
31
|
+
export interface Segmenter extends Process {
|
|
42
32
|
modelAsset: SegmentationModelAsset;
|
|
43
33
|
backgroundImage?: BackgroundImage;
|
|
44
34
|
open(options?: ProcessorOptions): Promise<void>;
|
|
@@ -54,5 +44,5 @@ export interface SegmentationParams extends RendererOptions {
|
|
|
54
44
|
width: number;
|
|
55
45
|
height: number;
|
|
56
46
|
}
|
|
57
|
-
export type SegmentationTransform = Transform<InputFrame, InputFrame> & SegmentationParams & Omit<Process, 'open'>;
|
|
47
|
+
export type SegmentationTransform = Transform<InputFrame, InputFrame> & SegmentationParams & Pick<Segmenter, 'update'> & Omit<Process, 'open'>;
|
|
58
48
|
export {};
|
|
@@ -56,8 +56,8 @@ export declare const createVideoElement: (width: number, height: number) => HTML
|
|
|
56
56
|
export declare const setVideoElementSrc: (video: HTMLVideoElement, src: MediaProvider) => void;
|
|
57
57
|
export declare const toVideoElement: (input: MediaStream, width: number, height: number) => HTMLVideoElement;
|
|
58
58
|
export declare const playVideo: (video: HTMLVideoElement, { setTimeout, clearTimeout }?: {
|
|
59
|
-
setTimeout?: (((handler: TimerHandler, timeout?: number
|
|
60
|
-
clearTimeout?: (((id: number | undefined) => void) & typeof clearTimeout) | undefined;
|
|
59
|
+
setTimeout?: (((handler: TimerHandler, timeout?: number, ...arguments: any[]) => number) & typeof globalThis.setTimeout) | undefined;
|
|
60
|
+
clearTimeout?: (((id: number | undefined) => void) & typeof globalThis.clearTimeout) | undefined;
|
|
61
61
|
}) => Promise<void>;
|
|
62
62
|
/**
|
|
63
63
|
* Load image with provided image element
|
package/dist/main/video/utils.js
CHANGED
|
@@ -81,9 +81,9 @@ export const setVideoElementSrc = (video, src) => {
|
|
|
81
81
|
}
|
|
82
82
|
};
|
|
83
83
|
if (src instanceof MediaStream) {
|
|
84
|
-
src.getVideoTracks()
|
|
84
|
+
for (const track of src.getVideoTracks()) {
|
|
85
85
|
track.addEventListener('ended', revokeObjectURL);
|
|
86
|
-
}
|
|
86
|
+
}
|
|
87
87
|
}
|
|
88
88
|
if ('MediaSource' in window && src instanceof MediaSource) {
|
|
89
89
|
src.addEventListener('sourceended', revokeObjectURL);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Transform } from '../types';
|
|
2
2
|
import type { InputFrame } from './types';
|
|
3
3
|
import type { ProcessVideoTrack } from './videoStreamTrackProcessor';
|
|
4
|
+
import type { ProcessorOptions } from '../../common';
|
|
4
5
|
export interface VideoProcessor {
|
|
5
|
-
open(): Promise<void>;
|
|
6
|
+
open(options?: ProcessorOptions): Promise<void>;
|
|
6
7
|
/**
|
|
7
8
|
* Process the video and return a MediaStream
|
|
8
9
|
*/
|
|
@@ -16,4 +17,4 @@ export interface VideoProcessor {
|
|
|
16
17
|
*/
|
|
17
18
|
destroy: () => Promise<void>;
|
|
18
19
|
}
|
|
19
|
-
export declare const createVideoProcessor: (transformers:
|
|
20
|
+
export declare const createVideoProcessor: (transformers: Transform<InputFrame, InputFrame>[], processTrack: ProcessVideoTrack) => VideoProcessor;
|
package/dist/main/video/video.js
CHANGED
|
@@ -4,8 +4,8 @@ export const createVideoProcessor = (transformers, processTrack) => {
|
|
|
4
4
|
const props = {
|
|
5
5
|
processing: false,
|
|
6
6
|
};
|
|
7
|
-
const open = async () => {
|
|
8
|
-
await Promise.all(transformers.map(transformer => transformer.init()));
|
|
7
|
+
const open = async (options) => {
|
|
8
|
+
await Promise.all(transformers.map(transformer => transformer.init(options)));
|
|
9
9
|
};
|
|
10
10
|
const process = async (source) => {
|
|
11
11
|
const [track] = source.getVideoTracks();
|
|
@@ -34,7 +34,9 @@ export const createVideoProcessor = (transformers, processTrack) => {
|
|
|
34
34
|
stopStreamTracks(props.outputStream);
|
|
35
35
|
props.outputStream = undefined;
|
|
36
36
|
props.processing = false;
|
|
37
|
-
|
|
37
|
+
for (const transformer of transformers) {
|
|
38
|
+
transformer.close();
|
|
39
|
+
}
|
|
38
40
|
};
|
|
39
41
|
const destroy = async () => {
|
|
40
42
|
close();
|
|
@@ -3,7 +3,7 @@ type Track = MediaStreamVideoTrack | MediaStreamVideoTrackGenerator;
|
|
|
3
3
|
interface Options {
|
|
4
4
|
signal?: AbortSignal;
|
|
5
5
|
}
|
|
6
|
-
export type ProcessVideoTrack = (track: MediaStreamVideoTrack, transformers:
|
|
6
|
+
export type ProcessVideoTrack = (track: MediaStreamVideoTrack, transformers: Transformer<InputFrame, InputFrame>[], options?: Options) => Promise<Track>;
|
|
7
7
|
/**
|
|
8
8
|
* Video track processor using MediaStreamTrackProcessor API
|
|
9
9
|
*/
|
|
@@ -18,9 +18,9 @@ export const createVideoTrackProcessor = () => (track, transformers, { signal }
|
|
|
18
18
|
const processor = createMediaStreamTrackProcessor({ track });
|
|
19
19
|
const trackGenerator = createMediaStreamTrackGenerator({ kind: 'video' });
|
|
20
20
|
let readable = processor.readable;
|
|
21
|
-
|
|
21
|
+
for (const transformer of transformers) {
|
|
22
22
|
readable = readable.pipeThrough(createStreamTransformer(adaptInputFrameTransformer(transformer)), { signal });
|
|
23
|
-
}
|
|
23
|
+
}
|
|
24
24
|
// Promise returned from `ReadableStream['pipeTo']` is only resolved
|
|
25
25
|
// when the streaming is finished
|
|
26
26
|
readable
|