move jpeg encoding to a worker thread pool
this also switches cvmts back to building with tsc, mostly because I couldn't get parcel's worker interop to work at all.
This commit is contained in:
@@ -5,22 +5,17 @@
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "parcel build src/index.ts --target node",
|
||||
"build": "tsc -outDir dist -rootDir src/",
|
||||
"serve": "node dist/index.js"
|
||||
},
|
||||
"author": "Elijah R, modeco80",
|
||||
"license": "GPL-3.0",
|
||||
"targets": {
|
||||
"node": {
|
||||
"context": "node",
|
||||
"outputFormat": "esmodule"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@computernewb/jpeg-turbo": "*",
|
||||
"@cvmts/qemu": "*",
|
||||
"execa": "^8.0.1",
|
||||
"mnemonist": "^0.39.5",
|
||||
"piscina": "^4.4.0",
|
||||
"sharp": "^0.33.3",
|
||||
"toml": "^3.0.0",
|
||||
"ws": "^8.14.1"
|
||||
@@ -28,7 +23,6 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.12.5",
|
||||
"@types/ws": "^8.5.5",
|
||||
"parcel": "^2.12.0",
|
||||
"prettier": "^3.2.5",
|
||||
"typescript": "^5.4.4"
|
||||
}
|
||||
|
||||
16
cvmts/src/JPEGEncoderWorker.ts
Normal file
16
cvmts/src/JPEGEncoderWorker.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
import jpegTurbo from "@computernewb/jpeg-turbo";
|
||||
import Piscina from "piscina";
|
||||
|
||||
export default async (opts: any) => {
|
||||
let res = await jpegTurbo.compress(opts.buffer, {
|
||||
format: jpegTurbo.FORMAT_RGBA,
|
||||
width: opts.width,
|
||||
height: opts.height,
|
||||
subsampling: jpegTurbo.SAMP_422,
|
||||
stride: opts.stride,
|
||||
quality: opts.quality
|
||||
});
|
||||
|
||||
return Piscina.move(res);
|
||||
}
|
||||
@@ -13,22 +13,20 @@ import { isIP } from 'node:net';
|
||||
import { QemuVM, QemuVmDefinition } from '@cvmts/qemu';
|
||||
import { IPData, IPDataManager } from './IPData.js';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import AuthManager from './AuthManager.js';
|
||||
import { Size, Rect, Logger } from '@cvmts/shared';
|
||||
|
||||
import jpegTurbo from "@computernewb/jpeg-turbo";
|
||||
import sharp from 'sharp';
|
||||
import Piscina from 'piscina';
|
||||
|
||||
// @ts-expect-error (I know, but this is already ugly)
|
||||
// really wish I didn't have to do it like this..
|
||||
const __dirname = fileURLToPath(new __parcel__URL__('..'));
|
||||
// Instead of strange hacks we can just use nodejs provided
|
||||
// import.meta properties, which have existed since LTS if not before
|
||||
const __filename = import.meta.filename;
|
||||
const __dirname = import.meta.dirname;
|
||||
|
||||
const kCVMTSAssetsRoot = path.resolve(__dirname, '../../assets');
|
||||
|
||||
console.log(__dirname);
|
||||
|
||||
// ejla this exist. Useing it.
|
||||
type ChatHistory = {
|
||||
user: string,
|
||||
msg: string
|
||||
@@ -46,19 +44,27 @@ function GetRawSharpOptions(size: Size): sharp.CreateRaw {
|
||||
}
|
||||
}
|
||||
|
||||
const kJpegPool = new Piscina({
|
||||
filename: path.join(import.meta.dirname + '/JPEGEncoderWorker.js'),
|
||||
minThreads: 4,
|
||||
maxThreads: 4
|
||||
});
|
||||
|
||||
async function EncodeJpeg(canvas: Buffer, displaySize: Size, rect: Rect): Promise<Buffer> {
|
||||
let offset = (rect.y * displaySize.width + rect.x) * 4;
|
||||
|
||||
//console.log('encoding rect', rect, 'with byteoffset', offset, '(size ', displaySize, ')');
|
||||
let res = await kJpegPool.run({
|
||||
buffer: canvas.subarray(offset),
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
stride: displaySize.width,
|
||||
quality: kJpegQuality
|
||||
});
|
||||
|
||||
return jpegTurbo.compress(canvas.subarray(offset), {
|
||||
format: jpegTurbo.FORMAT_RGBA,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
subsampling: jpegTurbo.SAMP_422,
|
||||
stride: displaySize.width,
|
||||
quality: kJpegQuality
|
||||
});
|
||||
|
||||
// have to manually turn it back into a buffer because
|
||||
// Piscina for some reason turns it into a Uint8Array
|
||||
return Buffer.from(res);
|
||||
}
|
||||
|
||||
export default class WSServer {
|
||||
@@ -125,8 +131,8 @@ export default class WSServer {
|
||||
this.voteCooldown = 0;
|
||||
this.turnsAllowed = true;
|
||||
this.screenHidden = false;
|
||||
this.screenHiddenImg = readFileSync(__dirname + "/../assets/screenhidden.jpeg").toString("base64");
|
||||
this.screenHiddenThumb = readFileSync(__dirname + "/../assets/screenhiddenthumb.jpeg").toString("base64");
|
||||
this.screenHiddenImg = readFileSync(path.join(kCVMTSAssetsRoot, "screenhidden.jpeg")).toString("base64");
|
||||
this.screenHiddenThumb = readFileSync(path.join(kCVMTSAssetsRoot, "screenhiddenthumb.jpeg")).toString("base64");
|
||||
|
||||
this.indefiniteTurn = null;
|
||||
this.ModPerms = Utilities.MakeModPerms(this.Config.collabvm.moderatorPermissions);
|
||||
@@ -937,7 +943,7 @@ export default class WSServer {
|
||||
|
||||
// TODO: pass custom options to Sharp.resize() probably
|
||||
let out = await sharp(display.Buffer(), {raw: GetRawSharpOptions(display.Size())})
|
||||
.resize(400, 300)
|
||||
.resize(400, 300, { fit: 'fill' })
|
||||
.toFormat('jpeg')
|
||||
.toBuffer();
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../tsconfig.json
|
||||
7
cvmts/tsconfig.json
Normal file
7
cvmts/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": [ "src/**/*" ],
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true,
|
||||
}
|
||||
}
|
||||
44
yarn.lock
44
yarn.lock
@@ -67,6 +67,7 @@ __metadata:
|
||||
execa: "npm:^8.0.1"
|
||||
mnemonist: "npm:^0.39.5"
|
||||
parcel: "npm:^2.12.0"
|
||||
piscina: "npm:^4.4.0"
|
||||
prettier: "npm:^3.2.5"
|
||||
sharp: "npm:^0.33.3"
|
||||
toml: "npm:^3.0.0"
|
||||
@@ -3155,6 +3156,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nice-napi@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "nice-napi@npm:1.0.2"
|
||||
dependencies:
|
||||
node-addon-api: "npm:^3.0.0"
|
||||
node-gyp: "npm:latest"
|
||||
node-gyp-build: "npm:^4.2.2"
|
||||
conditions: "!os=win32"
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-addon-api@npm:^3.0.0":
|
||||
version: 3.2.1
|
||||
resolution: "node-addon-api@npm:3.2.1"
|
||||
dependencies:
|
||||
node-gyp: "npm:latest"
|
||||
checksum: 10c0/41f21c9d12318875a2c429befd06070ce367065a3ef02952cfd4ea17ef69fa14012732f510b82b226e99c254da8d671847ea018cad785f839a5366e02dd56302
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-addon-api@npm:^6.0.0, node-addon-api@npm:^6.1.0":
|
||||
version: 6.1.0
|
||||
resolution: "node-addon-api@npm:6.1.0"
|
||||
@@ -3204,6 +3225,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-gyp-build@npm:^4.2.2":
|
||||
version: 4.8.0
|
||||
resolution: "node-gyp-build@npm:4.8.0"
|
||||
bin:
|
||||
node-gyp-build: bin.js
|
||||
node-gyp-build-optional: optional.js
|
||||
node-gyp-build-test: build-test.js
|
||||
checksum: 10c0/85324be16f81f0235cbbc42e3eceaeb1b5ab94c8d8f5236755e1435b4908338c65a4e75f66ee343cbcb44ddf9b52a428755bec16dcd983295be4458d95c8e1ad
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-gyp@npm:latest":
|
||||
version: 10.1.0
|
||||
resolution: "node-gyp@npm:10.1.0"
|
||||
@@ -3408,6 +3440,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"piscina@npm:^4.4.0":
|
||||
version: 4.4.0
|
||||
resolution: "piscina@npm:4.4.0"
|
||||
dependencies:
|
||||
nice-napi: "npm:^1.0.2"
|
||||
dependenciesMeta:
|
||||
nice-napi:
|
||||
optional: true
|
||||
checksum: 10c0/df6c2a2b673b0633a625f8dfc32f4519155e74ee24e31be9e69d2937e76d6cec8640278b4a50195652a943cccf8c634ed406f08598933c57e959d242b5fe5d1d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-value-parser@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "postcss-value-parser@npm:4.2.0"
|
||||
|
||||
Reference in New Issue
Block a user