简体   繁体   English

从 HTML Canvas 捕获图像的最快方法

[英]Fastest way to capture image from a HTML Canvas

Here's my code to capture an image from a Canvas playing video:这是我从 Canvas 播放视频中捕获图像的代码:

    let drawImage = function(time) {
        prevCtx.drawImage(videoPlayer, 0, 0, w, h);
        requestAnimationFrame(drawImage);
    }
    requestAnimationFrame(drawImage);

    let currIndex = 0;
    setInterval(function () {
        if(currIndex === 30) {
            currIndex = 0;
            console.log("Finishing video...");
            videoWorker.postMessage({action : "finish"});
        } else {
            console.log("Adding frame...");
            // w/o this `toDataURL` this loop runs at 30 cycle / second
            // so that means, this is the hot-spot and needs optimization:
            const base64img = preview.toDataURL(mimeType, 0.9);
            videoWorker.postMessage({ action: "addFrame", data: base64img});
            currIndex++;
        }
    }, 1000 / 30)

The goal is at each 30 frames (which should be at 1 second) it would trigger to transcode the frames added.目标是在每 30 帧(应该是 1 秒)时触发对添加的帧进行转码

The problem here is that the preview.toDataURL(mimeType, 0.9);这里的问题是preview.toDataURL(mimeType, 0.9); adds at least 1 second, without it the log shows the currIndex === 30 gets triggered every second.至少增加 1 秒,没有它,日志显示currIndex === 30每秒触发一次。 What would be the best approach to be able to capture at least about 30 FPS image.能够捕获至少大约 30 FPS 图像的最佳方法是什么。 What is the fastest way to capture image from a HTML Canvas that it will not be the bottleneck of real-time video transcoding process?从 HTML Canvas 捕获图像的最快方法是什么,它不会成为实时视频转码过程的瓶颈?

You should probably revise your project, because saving the whole video as still images will blow out the memory of most devices in no time.您可能应该修改您的项目,因为将整个视频保存为静止图像会立即炸毁大多数设备的 memory。 Instead have a look at MediaStreams and MediaRecorder APIs, which are able to do the transcoding and compression in real time.相反,请查看能够实时进行转码和压缩的MediaStreamsMediaRecorder API。 You can request a MediaStream from a canvas through its captureStream() method.您可以通过其captureStream()方法从 canvas 请求 MediaStream。


The fastest is probably to send an ImageBitmap to your Worker thread, these are really fast to generate from a canvas (simple copy of the pixel buffer), and can be transferred to your worker script, from where you should be able to draw it on a an OffscreenCanvas.最快的可能是将ImageBitmap发送到您的 Worker 线程,从 canvas(像素缓冲区的简单副本)生成这些非常快,并且可以传输到您的工作脚本,您应该可以从那里绘制它一个 OffscreenCanvas。

Main drawback: it's currently only supported in latest Chrome and Firefox (through webgl), and this can't be polyfilled...主要缺点:目前仅在最新的 Chrome 和 Firefox 中支持(通过 webgl),并且不能填充...

main.js main.js

else {
  console.log("Adding frame...");
  const bitmap = await createImageBitmap(preview);
  videoWorker.postMessage({ action: "addFrame", data: bitmap }, [bitmap]);
  currIndex++;
}

worker.js worker.js

const canvas = new OffscreenCanvas(width,height);
const ctx = canvas.getContext('2d'); // Chrome only
onmessage = async (evt) => {
  // ...
  ctx.drawImage( evt.data.data, 0, 0 );
  const image = await canvas.convertToBlob();
  storeImage(image);
};

An other option is to transfer an ImageData data.另一种选择是传输ImageData数据。 Not as fast as an ImageBitmap, it still has the advantage of not stopping your main thread with the compression part and since it can be transferred, the message to the Worker isn't computation heavy either.不如 ImageBitmap 快,它仍然具有不停止压缩部分的主线程的优点,并且由于它可以传输,因此发送给 Worker 的消息也不会计算繁重。
If you go this road, you may want to compress the data using something like pako (which uses the compression algorithm used by PNG images) from your Worker thread.如果您是 go 这条路,您可能希望使用 Worker 线程中的类似pako (使用 PNG 图像使用的压缩算法)之类的东西来压缩数据。

main.js main.js

else {
  console.log("Adding frame...");
  const img_data = prevCtx.getImageData(0,0,width,height);
  videoWorker.postMessage({ action: "addFrame", data: img_data }, [img_data.data]);
  currIndex++;
}

worker.js worker.js

onmessage = (evt) => {
 // ...
 const image = pako.deflate(evt.data.data); // compress to store
 storeImage(image);
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM