@pexip/media-processor 18.5.0 → 19.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 (34) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/common/backends/webgl/blender.js +3 -1
  3. package/dist/common/backends/webgl/blur.js +20 -5
  4. package/dist/common/backends/webgl/canvas.js +3 -0
  5. package/dist/common/backends/webgl/renderer.d.ts +2 -8
  6. package/dist/common/backends/webgl/renderer.js +46 -54
  7. package/dist/common/backends/webgl/smoothingMask.js +3 -1
  8. package/dist/common/backends/webgl/texture.js +3 -1
  9. package/dist/common/backends/webgl/textureToTexture.js +3 -1
  10. package/dist/common/backends/webgl/webglUtils.js +1 -0
  11. package/dist/common/canvasRenderUtils.d.ts +14 -9
  12. package/dist/common/canvasRenderUtils.js +26 -5
  13. package/dist/common/constants.d.ts +38 -6
  14. package/dist/common/constants.js +37 -6
  15. package/dist/common/tsconfig.tsbuildinfo +1 -1
  16. package/dist/common/types/messageEvents.d.ts +40 -5
  17. package/dist/common/types/processor.d.ts +13 -3
  18. package/dist/common/types/render.d.ts +11 -5
  19. package/dist/common/utils.d.ts +4 -3
  20. package/dist/common/utils.js +15 -6
  21. package/dist/main/tsconfig.tsbuildinfo +1 -1
  22. package/dist/main/video/canvasTransform.js +9 -0
  23. package/dist/main/video/constants.d.ts +1 -0
  24. package/dist/main/video/constants.js +1 -0
  25. package/dist/main/video/segmenters/mediapipe.d.ts +2 -1
  26. package/dist/main/video/segmenters/mediapipe.js +128 -34
  27. package/dist/main/video/transformer.js +14 -2
  28. package/dist/main/video/types.d.ts +4 -1
  29. package/dist/main/video/utils.d.ts +4 -1
  30. package/dist/main/video/utils.js +42 -15
  31. package/dist/main/video/videoStreamTrackProcessor.js +6 -11
  32. package/dist/workers/mediaWorker.js +14 -14
  33. package/dist/workers/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## 19.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e3a9d84: Update mediapipe and related asset to v0.10.20
8
+
9
+ ## 19.0.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 5b9daa4: Update @mediapipe/tasks-vision to 0.10.17
14
+ - 164a3e2: extract assert function
15
+ - 4fd97b2: Handle WebGL ContextLost & ContextRestored events
16
+
17
+ There is no way we could avoid context lost and we should have a robust
18
+ context lost handling in-place.
19
+
20
+ The following changes have been made:
21
+
22
+ - Handle WebGL context lost and restore events
23
+ - Dispatch custom event messages from WebWorker to Main thread
24
+ - Add debug capabilities to segmenter
25
+ - Allow Process Worker to restart processing
26
+ - Add reset() function to canvasRenderUtils
27
+ - Handle failed to create VideoFrame during the context lost
28
+ - Make Renderer stateless and Process Worker stateful
29
+ - Add restart function to Process Worker
30
+ - Ensure there is no processing when error occurs
31
+ - Workaround 2D Canvas context lost event bug
32
+ https://issues.chromium.org/issues/372756071
33
+
34
+ Related issues:
35
+
36
+ - Closes https://github.com/pexip/mcu/issues/39045
37
+ - Closes https://gitlab.com/pexip/zoo/-/issues/4896
38
+
39
+ Reference https://toji.dev/webgpu-best-practices/device-loss.html
40
+
41
+ - db258ca: Bump @mediapipe/task-visions to v0.10.18
42
+ - dfd1900: Timeout play video
43
+
44
+ ### Patch Changes
45
+
46
+ - 22a807b: Fix multi-trnasformer assertion
47
+ - Updated dependencies [164a3e2]
48
+ - Updated dependencies [b13010c]
49
+ - Updated dependencies [66a8f72]
50
+ - @pexip/utils@16.13.0
51
+
3
52
  ## 18.5.0
4
53
 
5
54
  ### Minor Changes
@@ -1,4 +1,5 @@
1
- import { assert } from '../../utils';
1
+ import { assert } from '@pexip/utils';
2
+ import { RENDERING_EVENTS } from '../../constants';
2
3
  import { m3 } from '../../m3';
3
4
  import { createGLProgram, createAndSetupTexture } from './webglUtils';
4
5
  import { vertexShaderSource } from './shaders/vertexShader';
@@ -61,6 +62,7 @@ export const createBlendRenderer = (processor) => {
61
62
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, outputTexture, 0);
62
63
  return {
63
64
  render: ({ mask, foreground, background, }) => {
65
+ assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
64
66
  gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
65
67
  gl.viewport(0, 0, processor.width, processor.height);
66
68
  gl.useProgram(program);
@@ -1,4 +1,6 @@
1
- import { assert, clamping } from '../../utils';
1
+ import { assert } from '@pexip/utils';
2
+ import { clamping } from '../../utils';
3
+ import { RENDERING_EVENTS } from '../../constants';
2
4
  import { m3 } from '../../m3';
3
5
  import { createAndSetupTexture, createGLProgram } from './webglUtils';
4
6
  import { vertexShaderSource } from './shaders/vertexShader';
@@ -84,6 +86,7 @@ export const createBlurRenderer = (processor, pass) => {
84
86
  const blurBuffers = generateBlurBuffers(pass + 1);
85
87
  return {
86
88
  render: (frame, pass) => {
89
+ assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
87
90
  gl.useProgram(program);
88
91
  gl.bindVertexArray(vao);
89
92
  // Frame texture
@@ -112,8 +115,14 @@ export const createBlurRenderer = (processor, pass) => {
112
115
  gl.uniformMatrix3fv(uniformLocations.matrix, false, matrix);
113
116
  gl.drawArrays(gl.TRIANGLES, 0, 6);
114
117
  const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
115
- if (status !== gl.FRAMEBUFFER_COMPLETE) {
116
- throw new Error(`downsampling FrameBuffer Status: ${status}`);
118
+ const error = gl.getError();
119
+ assert(error !== gl.CONTEXT_LOST_WEBGL, RENDERING_EVENTS.ContextLost);
120
+ assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
121
+ if (status !== gl.FRAMEBUFFER_COMPLETE ||
122
+ error !== gl.NO_ERROR) {
123
+ throw new Error(RENDERING_EVENTS.SamplingError, {
124
+ cause: `downsampling FrameBuffer Status: ${status}`,
125
+ });
117
126
  }
118
127
  // for the next draw, use the texture we just rendered to.
119
128
  gl.bindTexture(gl.TEXTURE_2D, texture);
@@ -130,8 +139,14 @@ export const createBlurRenderer = (processor, pass) => {
130
139
  gl.uniformMatrix3fv(uniformLocations.matrix, false, matrix);
131
140
  gl.drawArrays(gl.TRIANGLES, 0, 6);
132
141
  const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
133
- if (status !== gl.FRAMEBUFFER_COMPLETE) {
134
- throw new Error(`Upsampling FrameBuffer Status: ${status}`);
142
+ const error = gl.getError();
143
+ assert(error !== gl.CONTEXT_LOST_WEBGL, RENDERING_EVENTS.ContextLost);
144
+ assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
145
+ if (status !== gl.FRAMEBUFFER_COMPLETE ||
146
+ error !== gl.NO_ERROR) {
147
+ throw new Error(RENDERING_EVENTS.SamplingError, {
148
+ cause: `Upsampling FrameBuffer Status: ${status}`,
149
+ });
135
150
  }
136
151
  // for the next draw, use the texture we just rendered to.
137
152
  gl.bindTexture(gl.TEXTURE_2D, texture);
@@ -1,4 +1,6 @@
1
+ import { assert } from '@pexip/utils';
1
2
  import { m3 } from '../../m3';
3
+ import { RENDERING_EVENTS } from '../../constants';
2
4
  import { createGLProgram } from './webglUtils';
3
5
  import { vertexShaderSource } from './shaders/vertexShader';
4
6
  import { textureFragmentShaderSource } from './shaders/textureFragmentShader';
@@ -53,6 +55,7 @@ export const createCanvasRenderer = (processor) => {
53
55
  gl.vertexAttribPointer(attribLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
54
56
  return {
55
57
  render: (texture) => {
58
+ assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
56
59
  // Draw on the Canvas
57
60
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
58
61
  gl.viewport(0, 0, processor.width, processor.height);
@@ -1,8 +1,2 @@
1
- import type { Renderer, RendererOptions } from '../../types/render';
2
- import type { ImageMapIterable } from '../../types/media';
3
- interface Options extends Partial<RendererOptions> {
4
- backgroundImageInits?: ImageMapIterable;
5
- onError?: (error: Error) => void;
6
- }
7
- export declare const createRenderer: (canvas?: OffscreenCanvas, { backgroundBlurAmount, backgroundImageUrl, edgeBlurAmount, effects, foregroundThreshold, maskCombineRatio, onError, backgroundImageInits, }?: Options) => Renderer;
8
- export {};
1
+ import type { Renderer, RenderEventHandlers } from '../../types/render';
2
+ export declare const createRenderer: (canvas: OffscreenCanvas | undefined, renderEventHandlers: RenderEventHandlers) => Renderer;
@@ -1,5 +1,5 @@
1
- import { PROCESSING_WIDTH, PROCESSING_HEIGHT, EDGE_BLUR_AMOUNT, BACKGROUND_BLUR_AMOUNT, FOREGROUND_THRESHOLD, MASK_COMBINE_RATIO, RENDER_EFFECT, } from '../../constants';
2
- import { createObjectUpdater, createRemoteImageBitmap } from '../../utils';
1
+ import { assert } from '@pexip/utils';
2
+ import { PROCESSING_WIDTH, PROCESSING_HEIGHT, EDGE_BLUR_AMOUNT, BACKGROUND_BLUR_AMOUNT, RENDERING_EVENTS, } from '../../constants';
3
3
  import { toTextureInfo } from './webglUtils';
4
4
  import { createTextureRenderer } from './texture';
5
5
  import { createBlurRenderer } from './blur';
@@ -19,14 +19,14 @@ const createLazyProps = (stateBuilder) => {
19
19
  },
20
20
  });
21
21
  };
22
- export const createRenderer = (canvas = new OffscreenCanvas(PROCESSING_WIDTH, PROCESSING_HEIGHT), { backgroundBlurAmount = BACKGROUND_BLUR_AMOUNT, backgroundImageUrl = '', edgeBlurAmount = EDGE_BLUR_AMOUNT, effects = RENDER_EFFECT.None, foregroundThreshold = FOREGROUND_THRESHOLD, maskCombineRatio = MASK_COMBINE_RATIO, onError, backgroundImageInits, } = {}) => {
22
+ export const createRenderer = (canvas = new OffscreenCanvas(PROCESSING_WIDTH, PROCESSING_HEIGHT), renderEventHandlers) => {
23
23
  const rendererCreator = {
24
24
  videoFrameRenderer: () => createTextureRenderer(canvas),
25
25
  canvasRenderer: () => createCanvasRenderer(canvas),
26
26
  backgroundImageRenderer: () => createTextureRenderer(canvas),
27
27
  blendRenderer: () => createBlendRenderer(canvas),
28
- blurRenderer: () => createBlurRenderer(canvas, props.backgroundBlurAmount),
29
- maskBlurRenderer: () => createBlurRenderer(canvas, props.edgeBlurAmount),
28
+ blurRenderer: () => createBlurRenderer(canvas, BACKGROUND_BLUR_AMOUNT),
29
+ maskBlurRenderer: () => createBlurRenderer(canvas, EDGE_BLUR_AMOUNT),
30
30
  textureRenderer: () => createTextureToTextureRenderer(canvas),
31
31
  smoothingMaskRenderer: () => createSmoothingMaskRenderer(canvas),
32
32
  release: (states) => () => Object.keys(states).forEach(key => key !== 'release' &&
@@ -34,14 +34,8 @@ export const createRenderer = (canvas = new OffscreenCanvas(PROCESSING_WIDTH, PR
34
34
  };
35
35
  let lazyProps = createLazyProps(rendererCreator);
36
36
  const props = {
37
- backgroundBlurAmount,
38
- backgroundImageUrl,
39
- edgeBlurAmount,
40
- effects,
41
- foregroundThreshold,
42
- maskCombineRatio,
43
37
  canvas,
44
- backgroundImages: createRemoteImageBitmap(backgroundImageInits),
38
+ gl: null,
45
39
  // TODO: refactor to avoid repeated codes
46
40
  get videoFrameRenderer() {
47
41
  return lazyProps.videoFrameRenderer;
@@ -69,55 +63,45 @@ export const createRenderer = (canvas = new OffscreenCanvas(PROCESSING_WIDTH, PR
69
63
  return lazyProps.smoothingMaskRenderer;
70
64
  },
71
65
  };
72
- const updateProps = createObjectUpdater(props);
73
- const update = options => {
74
- if (options.backgroundImage) {
75
- props.backgroundImages.setImage(options.backgroundImage.key, options.backgroundImage.image);
76
- }
77
- updateProps(options);
66
+ const handleContextLost = (event) => {
67
+ event.preventDefault();
68
+ cleanup();
69
+ renderEventHandlers.contextLost(event.statusMessage);
78
70
  };
79
- const handleError = (error) => {
80
- if (error instanceof Error) {
81
- return onError?.(error);
82
- }
83
- throw error;
71
+ const handleContextRestored = (event) => {
72
+ renderEventHandlers.contextRestored(event.statusMessage);
84
73
  };
85
- const init = options => {
86
- if (options) {
87
- update(options);
88
- }
74
+ const handleContextCreationError = (event) => {
75
+ renderEventHandlers.contextCreationError(event.statusMessage);
76
+ };
77
+ const init = () => {
78
+ canvas.addEventListener('webglcontextlost', handleContextLost);
79
+ canvas.addEventListener('webglcontextrestored', handleContextRestored);
80
+ canvas.addEventListener('webglcontextcreationerror', handleContextCreationError);
81
+ props.gl = canvas.getContext('webgl2', {
82
+ premultipliedAlpha: false,
83
+ powerPreference: 'high-performance',
84
+ });
85
+ assert(props.gl, RENDERING_EVENTS.ContextCreationError);
89
86
  };
90
87
  const render = (mask, frame, options) => {
91
- if (options) {
92
- updateProps(options);
93
- }
88
+ assert(!props.gl?.isContextLost(), RENDERING_EVENTS.ContextLost);
94
89
  const smoothedMask = props.smoothingMaskRenderer.render({
95
90
  mask: toTextureInfo(mask),
96
91
  prevMask: props.prevMask,
97
- }, props);
92
+ }, options);
98
93
  const frameTexture = props.videoFrameRenderer.render(frame);
99
94
  let background = frameTexture;
100
- if (props.effects === 'blur' && props.backgroundBlurAmount) {
101
- background = props.blurRenderer.render(frameTexture, props.backgroundBlurAmount);
95
+ if (options.effects === 'blur' && options.backgroundBlurAmount) {
96
+ background = props.blurRenderer.render(frameTexture, options.backgroundBlurAmount);
102
97
  }
103
- else if (props?.effects === 'overlay' && props.backgroundImageUrl) {
104
- // Keep rendering with video frame until we get the background image
105
- const backgroundImage = props.backgroundImages.getImage(props.backgroundImageUrl);
106
- if (backgroundImage) {
107
- background = props.backgroundImageRenderer.render({
108
- frame: backgroundImage,
109
- timestamp: performance.now(),
110
- width: backgroundImage.width,
111
- height: backgroundImage.height,
112
- });
113
- }
114
- else {
115
- props.backgroundImages
116
- .tryFetchingImage(props.backgroundImageUrl)
117
- .catch(error => {
118
- handleError(new Error(`FailedToLoadBackgroundImage: ${props.backgroundImageUrl}`, { cause: error }));
119
- });
120
- }
98
+ else if (options?.effects === 'overlay' && options.backgroundImage) {
99
+ background = props.backgroundImageRenderer.render({
100
+ frame: options.backgroundImage,
101
+ timestamp: performance.now(),
102
+ width: options.backgroundImage.width,
103
+ height: options.backgroundImage.height,
104
+ });
121
105
  }
122
106
  const blendedFrameTexture = props.blendRenderer.render({
123
107
  mask: smoothedMask,
@@ -128,11 +112,19 @@ export const createRenderer = (canvas = new OffscreenCanvas(PROCESSING_WIDTH, PR
128
112
  // Cloned the current smoothedMask so it can be fed back to the next round
129
113
  props.prevMask = props.textureRenderer.render(smoothedMask);
130
114
  };
131
- const release = () => {
115
+ const cleanup = () => {
132
116
  lazyProps.release();
133
117
  lazyProps = createLazyProps(rendererCreator);
134
- props.backgroundImages.clear();
135
118
  props.prevMask = undefined;
136
119
  };
137
- return { init, render, release, update };
120
+ const release = () => {
121
+ canvas.removeEventListener('webglcontextlost', handleContextLost);
122
+ canvas.removeEventListener('webglcontextrestored', handleContextRestored);
123
+ canvas.removeEventListener('webglcontextcreationerror', handleContextCreationError);
124
+ cleanup();
125
+ };
126
+ const isContextLost = () => {
127
+ return !!props.gl?.isContextLost();
128
+ };
129
+ return { init, render, release, isContextLost };
138
130
  };
@@ -1,5 +1,6 @@
1
- import { assert } from '../../utils';
1
+ import { assert } from '@pexip/utils';
2
2
  import { m3 } from '../../m3';
3
+ import { RENDERING_EVENTS } from '../../constants';
3
4
  import { createGLProgram, createAndSetupTexture } from './webglUtils';
4
5
  import { vertexShaderSource } from './shaders/vertexShader';
5
6
  import { smoothingMaskFragmentShaderSource } from './shaders/smoothingMaskFragmentShader';
@@ -61,6 +62,7 @@ export const createSmoothingMaskRenderer = (processor) => {
61
62
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, outputTexture, 0);
62
63
  return {
63
64
  render: ({ mask, prevMask, }, options) => {
65
+ assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
64
66
  gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
65
67
  gl.viewport(0, 0, processor.width, processor.height);
66
68
  gl.clearColor(0, 0, 0, 0);
@@ -1,4 +1,5 @@
1
- import { assert } from '../../utils';
1
+ import { assert } from '@pexip/utils';
2
+ import { RENDERING_EVENTS } from '../../constants';
2
3
  import { m3 } from '../../m3';
3
4
  import { createAndSetupTexture, createGLProgram } from './webglUtils';
4
5
  import { vertexShaderSource } from './shaders/vertexShader';
@@ -60,6 +61,7 @@ export const createTextureRenderer = (processor) => {
60
61
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, outputTexture, 0);
61
62
  return {
62
63
  render: (video) => {
64
+ assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
63
65
  // Draw on the frame buffer
64
66
  gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
65
67
  gl.viewport(0, 0, processor.width, processor.height);
@@ -1,4 +1,5 @@
1
- import { assert } from '../../utils';
1
+ import { assert } from '@pexip/utils';
2
+ import { RENDERING_EVENTS } from '../../constants';
2
3
  import { m3 } from '../../m3';
3
4
  import { createAndSetupTexture, createGLProgram } from './webglUtils';
4
5
  import { vertexShaderSource } from './shaders/vertexShader';
@@ -59,6 +60,7 @@ export const createTextureToTextureRenderer = (processor) => {
59
60
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, outputTexture, 0);
60
61
  return {
61
62
  render: (inputTexture) => {
63
+ assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
62
64
  // Draw on the frame buffer
63
65
  gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
64
66
  gl.viewport(0, 0, processor.width, processor.height);
@@ -166,6 +166,7 @@ export const getLocations = (gl, program, locations) => Object.keys(locations).r
166
166
  export const createGLProgram = (processCanvas, { attribs, uniforms, vertexSrc, fragmentSrc, }) => {
167
167
  const gl = processCanvas.getContext('webgl2', {
168
168
  premultipliedAlpha: false,
169
+ powerPreference: 'high-performance',
169
170
  });
170
171
  if (!gl) {
171
172
  throw new Error('WebGL2 is not supported');
@@ -1,13 +1,17 @@
1
1
  import type { Segmentation } from './types/media';
2
- interface InternalCanvases {
3
- drawImageDataCanvas?: OffscreenCanvas;
4
- maskCanvas?: OffscreenCanvas;
5
- blurredMaskCanvas?: OffscreenCanvas;
6
- blurredCanvas?: OffscreenCanvas;
7
- downScaledCanvas?: OffscreenCanvas;
8
- backgroundImageCanvas?: OffscreenCanvas;
9
- inputCanvas?: OffscreenCanvas;
10
- }
2
+ declare const INTERNAL_CANVAS_KEYS: {
3
+ readonly drawImageDataCanvas: "drawImageDataCanvas";
4
+ readonly maskCanvas: "maskCanvas";
5
+ readonly blurredMaskCanvas: "blurredMaskCanvas";
6
+ readonly blurredCanvas: "blurredCanvas";
7
+ readonly downScaledCanvas: "downScaledCanvas";
8
+ readonly backgroundImageCanvas: "backgroundImageCanvas";
9
+ readonly inputCanvas: "inputCanvas";
10
+ };
11
+ type InternalCanvasKeys = keyof typeof INTERNAL_CANVAS_KEYS;
12
+ type InternalCanvases = {
13
+ [key in InternalCanvasKeys]?: OffscreenCanvas;
14
+ };
11
15
  export interface Point {
12
16
  x: number;
13
17
  y: number;
@@ -58,5 +62,6 @@ export declare const createCanvasRenderUtils: (processingWidth: number, processi
58
62
  offscreenCanvasName: keyof InternalCanvases;
59
63
  preserveOldDrawing?: boolean | undefined;
60
64
  }) => OffscreenCanvas;
65
+ reset: () => void;
61
66
  };
62
67
  export {};
@@ -1,4 +1,13 @@
1
1
  import { resize } from './utils';
2
+ const INTERNAL_CANVAS_KEYS = {
3
+ drawImageDataCanvas: 'drawImageDataCanvas',
4
+ maskCanvas: 'maskCanvas',
5
+ blurredMaskCanvas: 'blurredMaskCanvas',
6
+ blurredCanvas: 'blurredCanvas',
7
+ downScaledCanvas: 'downScaledCanvas',
8
+ backgroundImageCanvas: 'backgroundImageCanvas',
9
+ inputCanvas: 'inputCanvas',
10
+ };
2
11
  export const getCanvasRenderingContext2D = (canvas, options) => {
3
12
  const context = canvas.getContext('2d', options);
4
13
  if (!context) {
@@ -67,14 +76,15 @@ export const createCanvasRenderUtils = (processingWidth, processingHeight) => {
67
76
  const props = {
68
77
  downScaleFactor: 3,
69
78
  };
79
+ const createInternalCanvas = (canvasName) => createOffscreenCanvas(canvasName === 'downScaledCanvas'
80
+ ? Math.trunc(processingWidth / props.downScaleFactor)
81
+ : processingWidth, canvasName === 'downScaledCanvas'
82
+ ? Math.trunc(processingHeight / props.downScaleFactor)
83
+ : processingHeight);
70
84
  const getCanvas = (canvasName) => {
71
85
  const canvas = props[canvasName];
72
86
  if (!canvas) {
73
- const canvas = createOffscreenCanvas(canvasName === 'downScaledCanvas'
74
- ? Math.trunc(processingWidth / props.downScaleFactor)
75
- : processingWidth, canvasName === 'downScaledCanvas'
76
- ? Math.trunc(processingHeight / props.downScaleFactor)
77
- : processingHeight);
87
+ const canvas = createInternalCanvas(canvasName);
78
88
  props[canvasName] = canvas;
79
89
  return canvas;
80
90
  }
@@ -256,5 +266,16 @@ export const createCanvasRenderUtils = (processingWidth, processingHeight) => {
256
266
  renderImageToOffScreenCanvas,
257
267
  renderImageDataToOffScreenCanvas,
258
268
  drawAndBlurImageOnOffScreenCanvas,
269
+ reset: () => {
270
+ Object.keys(INTERNAL_CANVAS_KEYS).forEach(key => {
271
+ const canvasKey = key;
272
+ const canvas = props[canvasKey];
273
+ if (canvas && canvas instanceof OffscreenCanvas) {
274
+ // Reset Canvas by creating a new one as it is more reliable
275
+ // than setting the width and height to 0 in the situation of context lost
276
+ props[canvasKey] = createInternalCanvas(canvasKey);
277
+ }
278
+ });
279
+ },
259
280
  };
260
281
  };
@@ -5,17 +5,22 @@ export declare const FOREGROUND_THRESHOLD = 0.5;
5
5
  export declare const BACKGROUND_BLUR_AMOUNT = 3;
6
6
  export declare const EDGE_BLUR_AMOUNT = 3;
7
7
  export declare const MASK_COMBINE_RATIO = 0.5;
8
+ /**
9
+ * Process status
10
+ */
8
11
  export declare const PROCESS_STATUS: {
9
- readonly New: "new";
12
+ readonly Closed: "closed";
13
+ readonly Closing: "closing";
14
+ readonly Destroyed: "destroyed";
15
+ readonly Destroying: "destroying";
10
16
  readonly Error: "Error";
17
+ readonly Idle: "idle";
18
+ readonly New: "new";
11
19
  readonly Opened: "opened";
12
20
  readonly Opening: "opening";
13
21
  readonly Processing: "processing";
14
- readonly Idle: "idle";
15
- readonly Closing: "closing";
16
- readonly Closed: "closed";
17
- readonly Destroying: "destroying";
18
- readonly Destroyed: "destroyed";
22
+ readonly Updated: "updated";
23
+ readonly Updating: "updating";
19
24
  };
20
25
  export type ProcessStatus = ExtractValueTypes<typeof PROCESS_STATUS>;
21
26
  export declare const RENDER_EFFECT: {
@@ -24,4 +29,31 @@ export declare const RENDER_EFFECT: {
24
29
  readonly Overlay: "overlay";
25
30
  };
26
31
  export type RenderEffects = ExtractValueTypes<typeof RENDER_EFFECT>;
32
+ export declare const RENDERING_EVENTS: {
33
+ /**
34
+ * Triggered when failed to create a context
35
+ */
36
+ readonly ContextCreationError: "ContextCreationError";
37
+ /**
38
+ * Triggered when context is lost
39
+ */
40
+ readonly ContextLost: "ContextLost";
41
+ /**
42
+ * Triggered when context is restored
43
+ */
44
+ readonly ContextRestored: "ContextRestored";
45
+ /**
46
+ * Triggered when there is unknown error
47
+ */
48
+ readonly Error: "Error";
49
+ /**
50
+ * Triggered when the processor is restarted
51
+ */
52
+ readonly ProcessorRestarted: "ProcessorRestarted";
53
+ /**
54
+ * Triggered when sampling error occurs
55
+ */
56
+ readonly SamplingError: "SamplingError";
57
+ };
58
+ export type RenderingEvents = ExtractValueTypes<typeof RENDERING_EVENTS>;
27
59
  export {};
@@ -4,20 +4,51 @@ export const FOREGROUND_THRESHOLD = 0.5;
4
4
  export const BACKGROUND_BLUR_AMOUNT = 3; // Between 1 and 20
5
5
  export const EDGE_BLUR_AMOUNT = 3; // Between 0 and 20
6
6
  export const MASK_COMBINE_RATIO = 0.5;
7
+ /**
8
+ * Process status
9
+ */
7
10
  export const PROCESS_STATUS = {
8
- New: 'new',
11
+ Closed: 'closed',
12
+ Closing: 'closing',
13
+ Destroyed: 'destroyed',
14
+ Destroying: 'destroying',
9
15
  Error: 'Error',
16
+ Idle: 'idle',
17
+ New: 'new',
10
18
  Opened: 'opened',
11
19
  Opening: 'opening',
12
20
  Processing: 'processing',
13
- Idle: 'idle',
14
- Closing: 'closing',
15
- Closed: 'closed',
16
- Destroying: 'destroying',
17
- Destroyed: 'destroyed',
21
+ Updated: 'updated',
22
+ Updating: 'updating',
18
23
  };
19
24
  export const RENDER_EFFECT = {
20
25
  None: 'none',
21
26
  Blur: 'blur',
22
27
  Overlay: 'overlay',
23
28
  };
29
+ export const RENDERING_EVENTS = {
30
+ /**
31
+ * Triggered when failed to create a context
32
+ */
33
+ ContextCreationError: 'ContextCreationError',
34
+ /**
35
+ * Triggered when context is lost
36
+ */
37
+ ContextLost: 'ContextLost',
38
+ /**
39
+ * Triggered when context is restored
40
+ */
41
+ ContextRestored: 'ContextRestored',
42
+ /**
43
+ * Triggered when there is unknown error
44
+ */
45
+ Error: 'Error',
46
+ /**
47
+ * Triggered when the processor is restarted
48
+ */
49
+ ProcessorRestarted: 'ProcessorRestarted',
50
+ /**
51
+ * Triggered when sampling error occurs
52
+ */
53
+ SamplingError: 'SamplingError',
54
+ };