从 HTML Canvas 捕获图像的最快方法
Posted
技术标签:
【中文标题】从 HTML Canvas 捕获图像的最快方法【英文标题】:Fastest way to capture image from a HTML Canvas 【发布时间】:2020-07-25 12:24:59 【问题描述】:这是我从 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 图像的最佳方法是什么。 是什么,它不会成为实时视频转码过程的瓶颈?
【问题讨论】:
.toBlob
怎么样? .captureStream()
看起来也很有前途...
【参考方案1】:
您可能应该修改您的项目,因为将整个视频保存为静止图像会立即耗尽大多数设备的内存。相反,请查看MediaStreams 和MediaRecorder API,它们能够实时进行转码和压缩。您可以通过其captureStream()
方法从画布请求 MediaStream。
最快的可能是将ImageBitmap 发送到您的工作线程,从画布(像素缓冲区的简单副本)生成这些真的很快,并且可以转移到您的工作线程脚本,您应该可以在 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 的消息计算量也不大。 如果您走这条路,您可能希望使用 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);
;
【讨论】:
MedaiRecorder
的问题是它不能用于直播。我的意思是如果我使用它,录制的开始会延迟,录制后你必须停止它,它会以某种方式编码,这也很慢,因此,它不能用于生成 1 秒的视频块。例如,我需要每秒生成一个 1 秒的视频(AVI 或 MP4 任何可用的编解码器......)当我使用 MediaRecorded
时我不能这样做
为什么需要一秒钟的视频?不确定有多少玩家可以播放那些小视频。
为什么需要完整的视频文件?不能直接发几块吗?或者更好的是,只通过 WebRTC 发送 MediaStream。
为了更好地描述我想要实现的目标,打开此techsmith.com/blog/instructional-videos 并在检查开发人员工具以进行网络检查时播放视频,通过网络发送的 TS 视频文件是完整的视频。这是从流媒体服务器录制的视频,但在我的情况下,TS 文件将来自客户端浏览器网络摄像头,以类似方式流式传输到其他客户端。
我也不能在我的项目的这个阶段使用 WebRTC,因为这个阶段需要确保它首先与 WebSockets 一起工作。我已经测试了 Websockets 并且能够逐帧进行实时视频(JPEG 流),问题是它占用了带宽,所以需要先编码。以上是关于从 HTML Canvas 捕获图像的最快方法的主要内容,如果未能解决你的问题,请参考以下文章
Canvas:使用drawImage捕获流仅捕获流图像的左上角[重复]
HTML5 Canvas - 在屏幕上显示像素颜色数组的最快方式