这是我的代码,用于从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捕获图像的最快方法是什么,它不会成为实时视频转码过程的瓶颈?
答案 0 :(得分:1)
您可能应该修改项目,因为将整个视频保存为静止图像会立即耗尽大多数设备的内存。而是看一下MediaStreams和MediaRecorder API,它们能够实时进行转码和压缩。您可以通过其captureStream()
方法从画布请求MediaStream。
最快的方法是将ImageBitmap发送到您的Worker线程,这些线程确实可以从画布(像素缓冲区的简单副本)生成,并且可以传输到您的工作脚本,您应该可以在此处将其绘制在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的消息也不占用大量计算资源。
如果您走这条路,您可能想使用来自工作线程的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);
};