put mapbox-gl in a worker and read with readpixels() to encode in the main thread

106 views Asked by At

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 });
    }
  };
};
0

There are 0 answers