@pexip/media-processor 17.4.0 → 18.1.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/common/backends/webgl/blender.d.ts +11 -0
  3. package/dist/common/backends/webgl/blender.js +104 -0
  4. package/dist/common/backends/webgl/blur.d.ts +5 -0
  5. package/dist/common/backends/webgl/blur.js +153 -0
  6. package/dist/common/backends/webgl/canvas.d.ts +5 -0
  7. package/dist/common/backends/webgl/canvas.js +77 -0
  8. package/dist/common/backends/webgl/renderer.d.ts +8 -0
  9. package/dist/common/backends/webgl/renderer.js +126 -0
  10. package/dist/common/backends/webgl/shaders/blendFragmentShader.d.ts +1 -0
  11. package/dist/common/backends/webgl/shaders/blendFragmentShader.js +63 -0
  12. package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.d.ts +1 -0
  13. package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.js +45 -0
  14. package/dist/common/backends/webgl/shaders/textureFragmentShader.d.ts +1 -0
  15. package/dist/common/backends/webgl/shaders/textureFragmentShader.js +16 -0
  16. package/dist/common/backends/webgl/shaders/vertexShader.d.ts +1 -0
  17. package/dist/common/backends/webgl/shaders/vertexShader.js +19 -0
  18. package/dist/common/backends/webgl/texture.d.ts +6 -0
  19. package/dist/common/backends/webgl/texture.js +94 -0
  20. package/dist/common/backends/webgl/types.d.ts +5 -0
  21. package/dist/common/backends/webgl/types.js +1 -0
  22. package/dist/common/backends/webgl/webgl.mock.js +8 -0
  23. package/dist/common/backends/webgl/webglUtils.d.ts +80 -0
  24. package/dist/common/backends/webgl/webglUtils.js +194 -0
  25. package/dist/common/canvasRenderUtils.d.ts +62 -0
  26. package/dist/{main/video → common}/canvasRenderUtils.js +100 -61
  27. package/dist/common/constants.d.ts +27 -0
  28. package/dist/common/constants.js +23 -0
  29. package/dist/common/index.d.ts +9 -0
  30. package/dist/common/index.js +9 -0
  31. package/dist/common/m3.d.ts +17 -0
  32. package/dist/common/m3.js +69 -0
  33. package/dist/common/tsconfig.tsbuildinfo +1 -0
  34. package/dist/common/types/media.d.ts +22 -0
  35. package/dist/common/types/media.js +1 -0
  36. package/dist/common/types/messageEvents.d.ts +31 -0
  37. package/dist/common/types/messageEvents.js +1 -0
  38. package/dist/common/types/processor.d.ts +15 -0
  39. package/dist/common/types/processor.js +1 -0
  40. package/dist/common/types/render.d.ts +38 -0
  41. package/dist/common/types/render.js +1 -0
  42. package/dist/common/types/segmentation.d.ts +99 -0
  43. package/dist/common/types/segmentation.js +4 -0
  44. package/dist/common/types/utils.d.ts +19 -0
  45. package/dist/common/types/utils.js +1 -0
  46. package/dist/common/utils.d.ts +35 -0
  47. package/dist/common/utils.js +129 -0
  48. package/dist/main/benchUtils.d.ts +3 -2
  49. package/dist/main/benchUtils.js +7 -6
  50. package/dist/main/index.d.ts +2 -0
  51. package/dist/main/index.js +2 -0
  52. package/dist/main/process.d.ts +1 -11
  53. package/dist/main/process.js +0 -20
  54. package/dist/main/tsconfig.tsbuildinfo +1 -1
  55. package/dist/main/types.d.ts +2 -2
  56. package/dist/main/video/canvasTransform.d.ts +14 -5
  57. package/dist/main/video/canvasTransform.js +101 -86
  58. package/dist/main/video/constants.d.ts +0 -6
  59. package/dist/main/video/constants.js +0 -6
  60. package/dist/main/video/index.d.ts +2 -2
  61. package/dist/main/video/index.js +2 -2
  62. package/dist/main/video/load.d.ts +0 -14
  63. package/dist/main/video/load.js +0 -8
  64. package/dist/main/video/segmenters/mediapipe.d.ts +9 -11
  65. package/dist/main/video/segmenters/mediapipe.js +127 -77
  66. package/dist/main/video/typeGuards.d.ts +0 -1
  67. package/dist/main/video/typeGuards.js +2 -8
  68. package/dist/main/video/types.d.ts +19 -61
  69. package/dist/main/video/types.js +1 -20
  70. package/dist/main/video/utils.d.ts +5 -12
  71. package/dist/main/video/utils.js +5 -152
  72. package/dist/main/video/video.js +1 -0
  73. package/dist/main/video/videoStreamTrackProcessor.js +7 -4
  74. package/dist/workers/global.d.js +0 -0
  75. package/dist/workers/mediaWorker.d.ts +1 -0
  76. package/dist/workers/mediaWorker.js +188 -0
  77. package/dist/workers/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +21 -13
  79. package/dist/main/video/canvasRenderUtils.d.ts +0 -27
  80. package/dist/main/video/segmenters/index.d.ts +0 -1
  81. package/dist/main/video/segmenters/index.js +0 -1
  82. package/dist/workers/index.js +0 -1
  83. /package/dist/{workers/index.d.ts → common/backends/webgl/webgl.mock.d.ts} +0 -0
@@ -0,0 +1,99 @@
1
+ /// <reference types="offscreencanvas" />
2
+ export declare const SEGMENTATION_MODELS: {
3
+ readonly MediapipeSelfie: "selfie";
4
+ readonly DeepLabV3: "deeplabV3";
5
+ };
6
+ export type SegmentationModel = (typeof SEGMENTATION_MODELS)[keyof typeof SEGMENTATION_MODELS];
7
+ export interface SegmentationModelAsset {
8
+ path: string;
9
+ modelName: SegmentationModel;
10
+ }
11
+ export interface ImageSegmenterOptions {
12
+ /**
13
+ * The canvas element to bind textures to. This has to be set for GPU
14
+ * processing. The task will initialize a WebGL context and throw an error if
15
+ * this fails (e.g. if you have already initialized a different type of
16
+ * context).
17
+ */
18
+ canvas?: OffscreenCanvas;
19
+ modelAsset?: SegmentationModelAsset;
20
+ /** Overrides the default backend to use for the provided model. */
21
+ delegate?: 'CPU' | 'GPU' | undefined;
22
+ }
23
+ export interface SegmenterOptions {
24
+ basePath?: string;
25
+ output?: OffscreenCanvas | null;
26
+ processingWidth?: number;
27
+ processingHeight?: number;
28
+ imageSegmenterOptions?: ImageSegmenterOptions;
29
+ }
30
+ export interface SegmentationSmoothingConfig {
31
+ combineWithPreviousRatio: number;
32
+ }
33
+ /**
34
+ * Wrapper for a mask produced by a Segmentation Task.
35
+ */
36
+ export interface MPMask {
37
+ /** Returns the canvas element that the mask is bound to. */
38
+ readonly canvas: HTMLCanvasElement | OffscreenCanvas | undefined;
39
+ /** Returns the width of the mask. */
40
+ readonly width: number;
41
+ /** Returns the height of the mask. */
42
+ readonly height: number;
43
+ /**
44
+ * Returns whether this `MPMask` contains a mask of type `Uint8Array`.
45
+ */
46
+ hasUint8Array(): boolean;
47
+ /**
48
+ * Returns whether this `MPMask` contains a mask of type `Float32Array`.
49
+ */
50
+ hasFloat32Array(): boolean;
51
+ /**
52
+ * Returns whether this `MPMask` contains a mask of type `WebGLTexture`.
53
+ */
54
+ hasWebGLTexture(): boolean;
55
+ /**
56
+ * Returns the underlying mask as a Uint8Array`. Note that this involves an
57
+ * expensive GPU to CPU transfer if the current mask is only available as a
58
+ * `WebGLTexture`.
59
+ *
60
+ * @return The current data as a Uint8Array.
61
+ */
62
+ getAsUint8Array(): Uint8Array;
63
+ /**
64
+ * Returns the underlying mask as a single channel `Float32Array`. Note that
65
+ * this involves an expensive GPU to CPU transfer if the current mask is
66
+ * only available as a `WebGLTexture`.
67
+ *
68
+ * @return The current mask as a Float32Array.
69
+ */
70
+ getAsFloat32Array(): Float32Array;
71
+ /**
72
+ * Returns the underlying mask as a `WebGLTexture` object. Note that this
73
+ * involves a CPU to GPU transfer if the current mask is only available as
74
+ * a CPU array. The returned texture is bound to the current canvas (see
75
+ * `.canvas`).
76
+ *
77
+ * @return The current mask as a WebGLTexture.
78
+ */
79
+ getAsWebGLTexture(): WebGLTexture;
80
+ /**
81
+ * Creates a copy of the resources stored in this `MPMask`. You can
82
+ * invoke this method to extend the lifetime of a mask returned by a
83
+ * MediaPipe Task. Note that performance critical applications should aim to
84
+ * only use the `MPMask` within the MediaPipe Task callback so that
85
+ * copies can be avoided.
86
+ */
87
+ clone(): MPMask;
88
+ /**
89
+ * Frees up any resources owned by this `MPMask` instance.
90
+ *
91
+ * Note that this method does not free masks that are owned by the C++
92
+ * Task, as these are freed automatically once you leave the MediaPipe
93
+ * callback. Additionally, some shared state is freed only once you invoke
94
+ * the Task's `close()` method.
95
+ *
96
+ * @export
97
+ */
98
+ close(): void;
99
+ }
@@ -0,0 +1,4 @@
1
+ export const SEGMENTATION_MODELS = {
2
+ MediapipeSelfie: 'selfie',
3
+ DeepLabV3: 'deeplabV3',
4
+ };
@@ -0,0 +1,19 @@
1
+ export type ExtractMessageEventType<T extends {
2
+ type: string;
3
+ }> = T extends {
4
+ type: infer O;
5
+ } ? O : never;
6
+ export type IncludeMessageEventDataType<E extends {
7
+ type: string;
8
+ }, I> = E extends {
9
+ type: infer A;
10
+ data: infer D;
11
+ } ? A extends I ? D : never : E extends {
12
+ type: infer B;
13
+ } ? B extends I ? void : never : never;
14
+ /**
15
+ * From https://github.com/Microsoft/TypeScript/issues/26223#issuecomment-674500430
16
+ */
17
+ export type TupleOf<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
18
+ type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;
19
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ import type { SegmentationModel } from './types/segmentation';
2
+ import type { ImageMapIterable } from './types/media';
3
+ type Assert = <T>(t: T, message?: string) => asserts t is NonNullable<T>;
4
+ export declare const assert: Assert;
5
+ export declare const isSegmentationModel: (t: unknown) => t is SegmentationModel;
6
+ export declare const createObjectUpdater: <T extends { [K in keyof T]: unknown; }>(original: T) => (another: Partial<T>) => void;
7
+ export declare const createRemoteImageBitmap: (map?: ImageMapIterable) => {
8
+ tryFetchingImage: (uri: string) => Promise<ImageBitmap | null>;
9
+ setImage: (uri: string, image: ImageBitmap) => void;
10
+ getImage: (uri: string) => ImageBitmap | null;
11
+ removeImage: (uri: string) => boolean;
12
+ clear: () => void;
13
+ };
14
+ export declare const clamping: (min: number, max: number) => (value: number) => number;
15
+ export declare const calculateMaxBlurPass: (height: number) => number;
16
+ /**
17
+ * Calculate the coordinates and size of the source and destination to fit the
18
+ * target aspect ratio.
19
+ *
20
+ * @param fromWidth - The width of the source
21
+ * @param fromHeight - The height of the source
22
+ * @param toWidth - The width of the destination
23
+ * @param toHeight - The height of the destination
24
+ */
25
+ export declare const resize: (fromWidth: number, fromHeight: number, toWidth: number, toHeight: number) => {
26
+ sx: number;
27
+ sy: number;
28
+ sw: number;
29
+ sh: number;
30
+ dx: number;
31
+ dy: number;
32
+ dw: number;
33
+ dh: number;
34
+ };
35
+ export {};
@@ -0,0 +1,129 @@
1
+ import { SEGMENTATION_MODELS } from './types/segmentation';
2
+ export const assert = (t, message = 'AssertError') => {
3
+ if (t === undefined || t === null) {
4
+ throw new Error(message, { cause: t });
5
+ }
6
+ return t;
7
+ };
8
+ export const isSegmentationModel = (t) => {
9
+ if (typeof t === 'string' &&
10
+ Object.values(SEGMENTATION_MODELS).includes(t)) {
11
+ return true;
12
+ }
13
+ return false;
14
+ };
15
+ export const createObjectUpdater = (original) => {
16
+ const keys = new Set(Object.keys(original));
17
+ return (another) => {
18
+ for (const key in another) {
19
+ if (keys.has(key) && typeof original[key] === typeof another[key]) {
20
+ original[key] = another[key];
21
+ }
22
+ }
23
+ };
24
+ };
25
+ export const createRemoteImageBitmap = (map) => {
26
+ const cache = new Map(map);
27
+ const fetchingCache = new Set();
28
+ const fetchImage = async (uri) => {
29
+ const response = await fetch(uri);
30
+ const blob = await response.blob();
31
+ const image = await createImageBitmap(blob);
32
+ return image;
33
+ };
34
+ const tryFetchingImage = async (uri) => {
35
+ const image = cache.get(uri);
36
+ if (!image) {
37
+ if (fetchingCache.has(uri)) {
38
+ return null;
39
+ }
40
+ fetchingCache.add(uri);
41
+ const image = await fetchImage(uri);
42
+ cache.set(uri, image);
43
+ fetchingCache.delete(uri);
44
+ return image;
45
+ }
46
+ return image;
47
+ };
48
+ const removeImage = (uri) => {
49
+ const image = cache.get(uri);
50
+ image?.close();
51
+ return cache.delete(uri);
52
+ };
53
+ return {
54
+ tryFetchingImage,
55
+ setImage: (uri, image) => {
56
+ if (cache.has(uri)) {
57
+ removeImage(uri);
58
+ }
59
+ cache.set(uri, image);
60
+ },
61
+ getImage: (uri) => {
62
+ return cache.get(uri) ?? null;
63
+ },
64
+ removeImage,
65
+ clear: () => {
66
+ cache.forEach(img => img.close());
67
+ cache.clear();
68
+ },
69
+ };
70
+ };
71
+ export const clamping = (min, max) => (value) => Math.min(Math.max(value, min), max);
72
+ export const calculateMaxBlurPass = (height) => Math.trunc(Math.log2(height));
73
+ /**
74
+ * Calculate the coordinates and size of the source and destination to fit the
75
+ * target aspect ratio.
76
+ *
77
+ * @param fromWidth - The width of the source
78
+ * @param fromHeight - The height of the source
79
+ * @param toWidth - The width of the destination
80
+ * @param toHeight - The height of the destination
81
+ */
82
+ export const resize = (fromWidth, fromHeight, toWidth, toHeight) => {
83
+ if (!fromWidth ||
84
+ !fromHeight ||
85
+ !toWidth ||
86
+ !toHeight ||
87
+ fromWidth * toHeight === toWidth * fromHeight) {
88
+ return {
89
+ sx: 0,
90
+ sy: 0,
91
+ sw: fromWidth,
92
+ sh: fromHeight,
93
+ dx: 0,
94
+ dy: 0,
95
+ dw: toWidth,
96
+ dh: toHeight,
97
+ };
98
+ }
99
+ const originalAspectRatio = fromWidth / fromHeight;
100
+ const targetAspectRatio = toWidth / toHeight;
101
+ if (originalAspectRatio > targetAspectRatio) {
102
+ const sw = Math.ceil(fromWidth * targetAspectRatio) / originalAspectRatio;
103
+ const delta = Math.abs(fromWidth - sw);
104
+ const sx = Math.ceil(delta / 2);
105
+ return {
106
+ sx,
107
+ sy: 0,
108
+ sw,
109
+ sh: fromHeight,
110
+ dx: 0,
111
+ dy: 0,
112
+ dw: toWidth,
113
+ dh: toHeight,
114
+ };
115
+ }
116
+ const sh = Math.ceil(fromHeight * originalAspectRatio) / targetAspectRatio;
117
+ const delta = Math.abs(fromHeight - sh);
118
+ const sy = Math.ceil(delta / 2);
119
+ return {
120
+ sx: 0,
121
+ sy,
122
+ sw: fromWidth,
123
+ sh,
124
+ dx: 0,
125
+ dy: 0,
126
+ dw: toWidth,
127
+ dh: toHeight,
128
+ };
129
+ };
@@ -1,12 +1,13 @@
1
1
  import type { Clock } from './types';
2
+ type FPS = number;
3
+ type Timestamp = number;
2
4
  export interface Benchmark {
3
5
  begin(): void;
4
6
  end(): void;
5
- calculateFps(): number;
7
+ calculateFps(): [Timestamp, FPS];
6
8
  }
7
9
  interface BenchmarkOptions {
8
10
  calculationThresholdMS?: number;
9
11
  }
10
12
  export declare const createBenchmark: (clock?: Clock, { calculationThresholdMS }?: BenchmarkOptions) => Benchmark;
11
- export declare const calculateFps: (time: number) => number;
12
13
  export {};
@@ -18,17 +18,18 @@ export const createBenchmark = (clock = performance, { calculationThresholdMS =
18
18
  },
19
19
  calculateFps: () => {
20
20
  const calculateTime = clock.now();
21
- if (calculateTime - props.lastCalculateTime >=
22
- calculationThresholdMS) {
23
- const result = props.count === 0 ? 0 : props.sumDelta / props.count;
21
+ const lastDuration = calculateTime - props.lastCalculateTime;
22
+ if (lastDuration >= calculationThresholdMS) {
23
+ const result = props.count === 0
24
+ ? 0
25
+ : (lastDuration * props.count) / props.sumDelta;
24
26
  props.sumDelta = 0;
25
27
  props.count = 0;
26
28
  props.lastCalculateTime = calculateTime;
27
29
  props.lastResult = result;
28
- return result;
30
+ return [calculateTime, result];
29
31
  }
30
- return props.lastResult;
32
+ return [props.lastCalculateTime, props.lastResult];
31
33
  },
32
34
  };
33
35
  };
34
- export const calculateFps = (time) => time === 0 ? time : 1000.0 / time;
@@ -14,4 +14,6 @@ export { createAsyncCallbackLoop } from './utils';
14
14
  export * from './benchUtils';
15
15
  export declare const urls: {
16
16
  denoise: () => URL;
17
+ mediaWorker: () => URL;
17
18
  };
19
+ export * from '../common/index';
@@ -14,4 +14,6 @@ export { createAsyncCallbackLoop } from './utils';
14
14
  export * from './benchUtils';
15
15
  export const urls = {
16
16
  denoise: () => new URL('../worklets/denoise.worklet.js', import.meta.url),
17
+ mediaWorker: () => new URL('../workers/mediaWorker.js', import.meta.url),
17
18
  };
19
+ export * from '../common/index';
@@ -1,5 +1,5 @@
1
1
  import type { Queue } from '@pexip/utils';
2
- import type { StatsOptions, AudioSamples, AudioStats, Rect, Clock, IsVoice, ThrottleOptions } from './types';
2
+ import type { StatsOptions, AudioSamples, AudioStats, Clock, IsVoice, ThrottleOptions } from './types';
3
3
  /**
4
4
  * Default silent threshold
5
5
  * At least one LSB 16-bit data (compare is on absolute value).
@@ -191,16 +191,6 @@ export declare const isVoiceActivity: ({ volumeThreshold, VADTimeThreshold, cloc
191
191
  * @param heightB - The height of B
192
192
  */
193
193
  export declare const isEqualSize: (widthA: number, heightA: number, widthB: number, heightB: number) => boolean;
194
- /**
195
- * Convert the source size to destination size when necessary based on the
196
- * height
197
- *
198
- * @param sw - Source width
199
- * @param sh - Source height
200
- * @param dw - destination width
201
- * @param dh - destination height
202
- */
203
- export declare const fitDestinationSize: (sw: number, sh: number, dw: number, dh: number) => Rect;
204
194
  /**
205
195
  * A function to check provided time series data is considered as voice activity
206
196
  *
@@ -274,26 +274,6 @@ export const isVoiceActivity = ({ volumeThreshold = 0.05, VADTimeThreshold = 500
274
274
  * @param heightB - The height of B
275
275
  */
276
276
  export const isEqualSize = (widthA, heightA, widthB, heightB) => widthA === widthB && heightA === heightB;
277
- /**
278
- * Convert the source size to destination size when necessary based on the
279
- * height
280
- *
281
- * @param sw - Source width
282
- * @param sh - Source height
283
- * @param dw - destination width
284
- * @param dh - destination height
285
- */
286
- export const fitDestinationSize = (sw, sh, dw, dh) => {
287
- if (!sw || !sh || !dw || !dh) {
288
- return { x: 0, y: 0, width: 0, height: 0 };
289
- }
290
- if (isEqualSize(sw, sh, dw, dh)) {
291
- return { x: 0, y: 0, width: sw, height: sh };
292
- }
293
- const height = Math.floor(dw * (sh / sw));
294
- const y = Math.floor((dh - height) / 2);
295
- return { x: 0, y, width: dw, height };
296
- };
297
277
  /**
298
278
  * A function to check provided time series data is considered as voice activity
299
279
  *