Skip to content

Commit

Permalink
update the QOI code to use transferable object with memory leaks plug…
Browse files Browse the repository at this point in the history
…ged (#119)

Co-authored-by: ryan.kuba <ryan.kuba@kasmweb.com>
  • Loading branch information
thelamer and ryan.kuba authored Nov 20, 2024
1 parent db7bca4 commit 46412d2
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 42 deletions.
5 changes: 1 addition & 4 deletions app/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,12 @@ const UI = {

// Stream Quality Presets
let qualityDropdown = document.getElementById("noVNC_setting_video_quality");
let supportsSharedArrayBuffers = typeof SharedArrayBuffer !== "undefined";
qualityDropdown.appendChild(Object.assign(document.createElement("option"),{value:0,label:"Static"}))
qualityDropdown.appendChild(Object.assign(document.createElement("option"),{value:1,label:"Low"}))
qualityDropdown.appendChild(Object.assign(document.createElement("option"),{value:2,label:"Medium"}))
qualityDropdown.appendChild(Object.assign(document.createElement("option"),{value:3,label:"High"}))
qualityDropdown.appendChild(Object.assign(document.createElement("option"),{value:4,label:"Extreme"}))
if (supportsSharedArrayBuffers) {
qualityDropdown.appendChild(Object.assign(document.createElement("option"),{value:5,label:"Lossless"}))
}
qualityDropdown.appendChild(Object.assign(document.createElement("option"),{value:5,label:"Lossless"}))
qualityDropdown.appendChild(Object.assign(document.createElement("option"),{value:10,label:"Custom"}))

// if port == 80 (or 443) then it won't be present and should be
Expand Down
22 changes: 10 additions & 12 deletions core/decoders/qoi/decoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,7 @@ async function init(input) {
return wasm;
}

var arr;
var path;

async function run() {
self.addEventListener('message', async function(evt) {
if (evt.data.path) {
Expand All @@ -307,31 +305,31 @@ async function run() {
self.postMessage({
result: 1
})
} else if (evt.data.freemem) {
evt.data.freemem = null;
} else {
try {
let length = evt.data.length;
let data = new Uint8Array(evt.data.sab.slice(0, length));
let image = evt.data.image;
let data = new Uint8Array(image);
let resultData = decode_qoi(data);
if (!arr) {
arr = new Uint8Array(evt.data.sabR);
}
let lengthR = resultData.data.length;
arr.set(resultData.data);
let img = {
colorSpace: resultData.colorSpace,
width: resultData.width,
height: resultData.height
};
var buff = new ArrayBuffer(resultData.data.length);
new Uint8Array(buff).set(new Uint8Array(resultData.data));
self.postMessage({
result: 0,
img: img,
length: lengthR,
width: evt.data.width,
height: evt.data.height,
x: evt.data.x,
y: evt.data.y,
frame_id: evt.data.frame_id
});
frame_id: evt.data.frame_id,
data: buff,
freemem: evt.data.image
}, [buff]);
} catch (err) {
self.postMessage({
result: 2,
Expand Down
35 changes: 9 additions & 26 deletions core/decoders/tight.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,17 @@ export default class TightDecoder {
let i = this._availableWorkers.pop();
let worker = this._workers[i];
let rect = this._qoiRects.shift();
this._arrs[i].set(rect.data);
var image = new ArrayBuffer(rect.data.length);
new Uint8Array(image).set(new Uint8Array(rect.data));
worker.postMessage({
length: rect.data.length,
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
depth: rect.depth,
sab: this._sabs[i],
sabR: this._sabsR[i],
frame_id: rect.frame_id
});
frame_id: rect.frame_id,
image: image
}, [image]);
}
}

Expand Down Expand Up @@ -471,10 +470,6 @@ export default class TightDecoder {
if (this._workers) {
this._enableQOI = false;
this._availableWorkers = null;
this._sabs = null;
this._sabsR = null;
this._arrs = null;
this._arrsR = null;
this._qoiRects = null;
this._rectQlooping = null;
for await (let i of Array.from(Array(this._threads).keys())) {
Expand All @@ -486,12 +481,6 @@ export default class TightDecoder {
}

_enableQOIWorkers() {
const supportsSharedArrayBuffers = typeof SharedArrayBuffer !== "undefined";
if (!supportsSharedArrayBuffers) {
Log.Warn("Enabling QOI Failed, client not compatible.");
return false;
}

let fullPath = window.location.pathname;
let path = fullPath.substring(0, fullPath.lastIndexOf('/')+1);
if ((window.navigator.hardwareConcurrency) && (window.navigator.hardwareConcurrency >= 4)) {
Expand All @@ -501,24 +490,16 @@ export default class TightDecoder {
}
this._workers = [];
this._availableWorkers = [];
this._sabs = [];
this._sabsR = [];
this._arrs = [];
this._arrsR = [];
this._qoiRects = [];
this._rectQlooping = false;
for (let i = 0; i < this._threads; i++) {
this._workers.push(new Worker("core/decoders/qoi/decoder.js"));
this._sabs.push(new SharedArrayBuffer(300000));
this._sabsR.push(new SharedArrayBuffer(400000));
this._arrs.push(new Uint8Array(this._sabs[i]));
this._arrsR.push(new Uint8ClampedArray(this._sabsR[i]));
this._workers[i].onmessage = (evt) => {
this._availableWorkers.push(i);
switch(evt.data.result) {
case 0:
let data = new Uint8ClampedArray(evt.data.length);
data.set(this._arrsR[i].slice(0, evt.data.length));
evt.data.freemem = null;
let data = new Uint8ClampedArray(evt.data.data);
let img = new ImageData(data, evt.data.img.width, evt.data.img.height, {colorSpace: evt.data.img.colorSpace});

this._displayGlobal.blitQoi(
Expand All @@ -532,6 +513,8 @@ export default class TightDecoder {
false
);
this._processRectQ();
// Send data back for garbage collection
this._workers[i].postMessage({freemem: evt.data.data});
break;
case 1:
Log.Info("QOI Worker is now available.");
Expand Down

0 comments on commit 46412d2

Please sign in to comment.