Skip to content

Commit f4eebf6

Browse files
committedApr 3, 2022
add workerbase
1 parent 9ecde50 commit f4eebf6

File tree

11 files changed

+330
-0
lines changed

11 files changed

+330
-0
lines changed
 

‎000_WorkerBase/.eslintrc.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module.exports = {
2+
env: {
3+
browser: true,
4+
es2021: true,
5+
node: true,
6+
},
7+
extends: [
8+
"eslint:recommended",
9+
"plugin:react/recommended",
10+
"plugin:@typescript-eslint/recommended",
11+
],
12+
parser: "@typescript-eslint/parser",
13+
parserOptions: {
14+
ecmaFeatures: {
15+
jsx: true,
16+
},
17+
ecmaVersion: 13,
18+
sourceType: "module",
19+
},
20+
plugins: ["react", "@typescript-eslint"],
21+
rules: {},
22+
};

‎000_WorkerBase/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/node_modules
2+
/dist
3+
4+
*#
5+
*~

‎000_WorkerBase/.prettierrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"tabWidth": 4,
3+
"useTabs": false,
4+
"semi": true,
5+
"printWidth": 360
6+
}

‎000_WorkerBase/package-lock.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎000_WorkerBase/package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "000_WorkerBase",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "dist/index.js",
6+
"scripts": {
7+
"build": "tsc",
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"keywords": [],
11+
"author": "",
12+
"license": "ISC",
13+
"devDependencies": {
14+
"@types/node": "^17.0.23",
15+
"typescript": "^4.6.3",
16+
"@types/offscreencanvas": "^2019.6.4"
17+
},
18+
"dependencies": {}
19+
}

‎000_WorkerBase/src/BlockingQueue.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export class BlockingQueue<T> {
2+
private _promises: Promise<T>[];
3+
private _resolvers: ((t: T) => void)[];
4+
5+
constructor() {
6+
this._resolvers = [];
7+
this._promises = [];
8+
}
9+
10+
private _add() {
11+
this._promises.push(
12+
new Promise((resolve) => {
13+
this._resolvers.push(resolve);
14+
})
15+
);
16+
}
17+
18+
enqueue(t: T) {
19+
if (!this._resolvers.length) this._add();
20+
const resolve = this._resolvers.shift()!;
21+
resolve(t);
22+
}
23+
24+
dequeue() {
25+
if (!this._promises.length) this._add();
26+
const promise = this._promises.shift()!;
27+
return promise;
28+
}
29+
30+
isEmpty() {
31+
return !this._promises.length;
32+
}
33+
34+
isBlocked() {
35+
return !!this._resolvers.length;
36+
}
37+
38+
get length() {
39+
return this._promises.length - this._resolvers.length;
40+
}
41+
}

‎000_WorkerBase/src/BrowserUtil.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/************************************************
2+
* THIS FILE SHOULD BE EDITED IN COMMON FOLDER. *
3+
************************************************/
4+
5+
export const BrowserTypes = {
6+
MSIE: "MSIE",
7+
EDGE: "EDGE",
8+
CHROME: "CHROME",
9+
SAFARI: "SAFARI",
10+
FIREFOX: "FIREFOX",
11+
OPERA: "OPERA",
12+
OTHER: "OTHER",
13+
} as const;
14+
export type BrowserTypes = typeof BrowserTypes[keyof typeof BrowserTypes];
15+
16+
export const getBrowserType = () => {
17+
var userAgent = window.navigator.userAgent.toLowerCase();
18+
if (userAgent.indexOf("msie") !== -1 || userAgent.indexOf("trident") !== -1) {
19+
return BrowserTypes.MSIE;
20+
} else if (userAgent.indexOf("edge") !== -1) {
21+
return BrowserTypes.EDGE;
22+
} else if (userAgent.indexOf("chrome") !== -1) {
23+
return BrowserTypes.CHROME;
24+
} else if (userAgent.indexOf("safari") !== -1) {
25+
return BrowserTypes.SAFARI;
26+
} else if (userAgent.indexOf("firefox") !== -1) {
27+
return BrowserTypes.FIREFOX;
28+
} else if (userAgent.indexOf("opera") !== -1) {
29+
return BrowserTypes.OPERA;
30+
} else {
31+
return BrowserTypes.OTHER;
32+
}
33+
};
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { BlockingQueue } from "./BlockingQueue";
2+
import { BrowserTypes, getBrowserType } from "./BrowserUtil";
3+
import { WorkerCommand, WorkerResponse } from "./const";
4+
5+
export abstract class LocalWorker {
6+
abstract init: (config: any | null) => Promise<void>;
7+
abstract predict: (config: any, params: any, targets: any) => Promise<any>;
8+
}
9+
10+
export type WorkerManagerBaseInitProps = {
11+
useWorkerForSafari: boolean;
12+
processOnLocal: boolean;
13+
localWorker?: () => LocalWorker;
14+
workerJs?: () => Worker;
15+
};
16+
17+
export abstract class WorkerManagerBase {
18+
abstract localWorker: LocalWorker;
19+
worker: Worker | null = null;
20+
21+
abstract init: (config: any | null) => Promise<void>;
22+
abstract predict: (params: any, targets: any) => Promise<any>;
23+
24+
sem = new BlockingQueue<number>();
25+
26+
constructor() {
27+
this.sem.enqueue(0);
28+
}
29+
30+
lock = async () => {
31+
const num = await this.sem.dequeue();
32+
return num;
33+
};
34+
unlock = (num: number) => {
35+
this.sem.enqueue(num + 1);
36+
};
37+
38+
initCommon = async (props: WorkerManagerBaseInitProps, config: any) => {
39+
const num = await this.lock();
40+
if (this.worker) {
41+
this.worker.terminate();
42+
}
43+
this.worker = null;
44+
45+
if (this.useWorker(props) === false) {
46+
await this.localWorker.init(config);
47+
this.unlock(num);
48+
return;
49+
}
50+
51+
const newWorker: Worker = props.workerJs!();
52+
53+
const p = new Promise<void>((resolve, reject) => {
54+
newWorker.onmessage = (event) => {
55+
if (event.data.message === WorkerResponse.INITIALIZED) {
56+
this.worker = newWorker;
57+
resolve();
58+
} else {
59+
console.log("Bodypix Initialization something wrong..");
60+
reject();
61+
}
62+
};
63+
newWorker.postMessage({
64+
message: WorkerCommand.INITIALIZE,
65+
config: config,
66+
});
67+
});
68+
try {
69+
await p;
70+
} catch (err) {
71+
console.log("worker initialize error");
72+
} finally {
73+
this.unlock(num);
74+
}
75+
return;
76+
};
77+
78+
generateImageBitmap = (target: HTMLCanvasElement | HTMLVideoElement, width: number, height: number) => {
79+
if (target.width <= 0 || target.height <= 0) {
80+
console.log("target canvas|videois invalid", target);
81+
throw new Error("target canvas|video is invalid");
82+
}
83+
const offscreen = new OffscreenCanvas(width, height);
84+
const offctx = offscreen.getContext("2d")!;
85+
offctx.drawImage(target, 0, 0, width, height);
86+
const imageBitmap = offscreen.transferToImageBitmap();
87+
return imageBitmap;
88+
};
89+
90+
private targetCanvas: HTMLCanvasElement = document.createElement("canvas");
91+
generateTargetCanvas = (target: HTMLCanvasElement | HTMLVideoElement, width: number, height: number) => {
92+
if (target.width <= 0 || target.height <= 0) {
93+
console.log("target canvas|videois invalid", target);
94+
throw new Error("target canvas|video is invalid");
95+
}
96+
this.targetCanvas.width = width;
97+
this.targetCanvas.height = height;
98+
const ctx = this.targetCanvas.getContext("2d")!;
99+
ctx.drawImage(target, 0, 0, width, height);
100+
return this.targetCanvas;
101+
};
102+
103+
sendToWorker = async (config: any, params: any, data: any) => {
104+
if (this.sem.length > 100) {
105+
throw new Error(`queue is fulled: ${this.sem.length}`);
106+
}
107+
const num = await this.lock();
108+
const p = new Promise((resolve, reject) => {
109+
this.worker!.onmessage = (event) => {
110+
if (event.data.message === WorkerResponse.PREDICTED) {
111+
resolve(event.data.prediction);
112+
} else {
113+
console.log("Bodypix Prediction something wrong..", event.data.message);
114+
reject(event);
115+
}
116+
};
117+
this.worker!.postMessage(
118+
{
119+
message: WorkerCommand.PREDICT,
120+
config: config,
121+
params: params,
122+
data: data,
123+
},
124+
[data]
125+
);
126+
});
127+
let prediction;
128+
try {
129+
prediction = await p;
130+
} catch (error) {
131+
console.log("worker prediction error");
132+
} finally {
133+
this.unlock(num);
134+
}
135+
return prediction;
136+
};
137+
138+
private useWorker = (props: WorkerManagerBaseInitProps) => {
139+
if (props.processOnLocal) {
140+
return false;
141+
} else if (props.useWorkerForSafari === false && getBrowserType() === BrowserTypes.SAFARI) {
142+
return false;
143+
} else {
144+
return true;
145+
}
146+
};
147+
}

‎000_WorkerBase/src/const.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const WorkerCommand = {
2+
INITIALIZE: "initialize",
3+
PREDICT: "predict",
4+
} as const;
5+
export type WorkerCommand = typeof WorkerCommand[keyof typeof WorkerCommand];
6+
7+
export const WorkerResponse = {
8+
INITIALIZED: "initialized",
9+
PREDICTED: "predicted",
10+
} as const;
11+
export type WorkerResponse = typeof WorkerResponse[keyof typeof WorkerResponse];

‎000_WorkerBase/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { WorkerManagerBase, LocalWorker } from "./WorkerManagerBase";
2+
import { getBrowserType, BrowserTypes } from "./BrowserUtil";
3+
4+
export { WorkerManagerBase, LocalWorker };
5+
export { getBrowserType, BrowserTypes };

‎000_WorkerBase/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"module": "commonjs",
5+
"lib": ["es2018", "dom"],
6+
"declaration": true,
7+
"sourceMap": true,
8+
"outDir": "./dist",
9+
10+
"strict": true,
11+
"esModuleInterop": true,
12+
"skipLibCheck": true,
13+
"forceConsistentCasingInFileNames": true
14+
}
15+
}

0 commit comments

Comments
 (0)
Please sign in to comment.