繁体   English   中英

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

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

这是我从 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)

目标是在每 30 帧(应该是 1 秒)时触发对添加的帧进行转码

这里的问题是preview.toDataURL(mimeType, 0.9); 至少增加 1 秒,没有它,日志显示currIndex === 30每秒触发一次。 能够捕获至少大约 30 FPS 图像的最佳方法是什么。 从 HTML Canvas 捕获图像的最快方法是什么,它不会成为实时视频转码过程的瓶颈?

您可能应该修改您的项目,因为将整个视频保存为静止图像会立即炸毁大多数设备的 memory。 相反,请查看能够实时进行转码和压缩的MediaStreamsMediaRecorder API。 您可以通过其captureStream()方法从 canvas 请求 MediaStream。


最快的可能是将ImageBitmap发送到您的 Worker 线程,从 canvas(像素缓冲区的简单副本)生成这些非常快,并且可以传输到您的工作脚本,您应该可以从那里绘制它一个 OffscreenCanvas。

主要缺点:目前仅在最新的 Chrome 和 Firefox 中支持(通过 webgl),并且不能填充...

main.js

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

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

另一种选择是传输ImageData数据。 不如 ImageBitmap 快,它仍然具有不停止压缩部分的主线程的优点,并且由于它可以传输,因此发送给 Worker 的消息也不会计算繁重。
如果您是 go 这条路,您可能希望使用 Worker 线程中的类似pako (使用 PNG 图像使用的压缩算法)之类的东西来压缩数据。

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

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