This is the first time I've asked a question on Stackoverflow and it's because I really got stuck. I have an animation in mapbox, which runs on the same thread as the h264-mp4-encoder video encoder. My code works and does the job, but during the rendering of the mapbox animation you see small jumps and sometimes vibrations and I am sure that it is because both tasks are carried out in the same thread. I tried to create a worker for encoding, but I have errors because the encoder needs the mapbox instance. I was looking into offscreencanvas but there isn't much information about working with webgl. Maybe you could recommend a different way to deal with that problem, either by sending the encoder to a worker or mapbox to another worker or even better if you opened up a better way to solve this.
map.on("load", async () => {
// add 3d, sky and fog
add3D();
await map.once("idle");
// don't forget to enable WebAssembly SIMD in chrome://flags for faster encoding
const supportsSIMD = await simd();
// initialize H264 video encoder
const Encoder = await loadEncoder({ simd: supportsSIMD });
const gl = map.painter.context.gl;
const width =
gl.drawingBufferWidth % 2 === 0
? gl.drawingBufferWidth
: gl.drawingBufferWidth - 1;
const height =
gl.drawingBufferHeight % 2 === 0
? gl.drawingBufferHeight
: gl.drawingBufferHeight - 1;
//create encoder
const encoder = Encoder.create({
width,
height,
fps: 30,
kbps: 2200,
rgbFlipY: true,
});
// stub performance.now for deterministic rendering per-frame (only available in dev build)
let now = performance.now();
//mapboxgl.setNow(now);
const ptr = encoder.getRGBPointer(); // keep a pointer to encoder WebAssembly heap memory
function frame() { //this function is called to read the pixels every time mapbox re-renders
//increment stub time by 16.6ms (60 fps)
now += 1000 / 30;
mapboxgl.setNow(now);
const pixels = encoder.memory().subarray(ptr); // get a view into encoder memory
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); // read pixels into encoder
console.log("inicia");
encoder.encodeRGBPointer(); // encode the frame
}
map.on("render", frame); // set up frame-by-frame
await playAnimations(geojson);//a call is made to the map animation function
// stop recording
map.off("render", frame);
mapboxgl.restoreNow();
setIsRunAnimate(false);
//encoded video
const mp4Blob = encoder.end();
setVideoBlob(mp4Blob);
})
This is a worker who believes, thinking he could achieve it. What I tried was to read the pixels inside the worker and encode it, but it gives me an error because it requires mapboxgl.
// encoderWorker.js
let encoding = false;
let now = performance.now();
let ptr;
onmessage = async (event) => {
const { width, height, fps, kbps, rgbFlipY } = event.data;
const { simd } = await import("wasm-feature-detect");
const Encoder = await import("../../assets/convertidor.js");
const encoder = Encoder.create({
width,
height,
fps,
kbps,
rgbFlipY,
});
ptr = encoder.getRGBPointer();
function encodeFrame() {
now += 1000 / fps;
mapboxgl.setNow(now);
const pixels = encoder.memory().subarray(ptr);
encoder.encodeRGBPointer();
}
onmessage = (event) => {
const { type } = event.data;
if (type === "start" && !encoding) {
// Start coding/frame-by-frame
encoding = true;
encodeFrame();
requestAnimationFrame(encodeFrame);
} else if (type === "finish" && encoding) {
// Stop encryption
encoding = false;
// Return the encoded blob to the main thread
const encodedBlob = encoder.end();
postMessage({ type: "complete", encodedBlob });
}
};
};