将HTML Canvas / WebGLanimation服务器端录制到video中的最佳方法?

我有一套可以在Canvas(fabric.js)或WebGL(three.js)中制作的animation。 我需要通过脚本自动logging它们,服务器端,并输出一个video文件。

animation包括:

  1. 图片
  2. video(带audio)
  3. 其他animation/效果

在过去几个月里,我已经研究了很多。

结果
1. 用户PhantomJS + FFMPEG
在无头浏览器(PhantomJS)上运行HTML Canvasanimation并用FFMPEGlogging。 这里的问题是PhantomJS既不支持WebGL也不支持Video元素。 http://phantomjs.org/supported-web-standards.html

2. 使用Websockets使用DataURL将数据发送回服务器
在这里,我们还需要在浏览器上运行animation( 我们不能因为我们必须在服务器上做所有事情 )。

3. 使用节点canvas
这是TJ Holowaychuk的一个库,它允许在Node.js上渲染HTML Canvas。 但它有其自身的局限性,加上我没有真正探索过这个领域。 (如果有人可以更多地了解这个图书馆)

如果有人以前做过,或可以指导我有用的地方。
我们所需要做的就是使用一些数据来创buildanimation并将其logging到video中,而服务器端的所有内容都将logging下来。

您可以使用electron来渲染WebGL页面,将BrowserWindow选项“show”设置为false,并且/或者使用xvfb-run来运行headless。

我不认为node-canvas支持webgl上下文,所以你将不得不使用围绕2D绘图的库,它当然不会支持任何video编解码器。

如果您可以使用节点canvas使您的animation工作,则可以以适合您的内容的速率抓取animation帧,如下所示:

披露 :我已经成功地使用FFmpeg来编码一系列外部生成的图像,但还没有尝试下面的setInterval()方法。 除了animation开销本身之外,我不知道如何以30 FPS将canvas导出为PNG文件。

 // assuming "canvas" is asynchronously drawn on some interval function saveCanvas(canvas, destFile) { return new Promise((resolve, reject) => { const ext = path.extname(destFile), encoder = '.png' === ext ? 'pngStream' : 'jpegStream'; let writable = fs.createWriteStream(destFile), readable = canvas[encoder](); writable .on('finish', resolve) .on('error', err => { let msg = `cannot write "${destFile}": ${err.message}`; reject(new Error(msg)); }); readable .on('end', () => writable.end()) .on('error', err => { let msg = `cannot encode "${destFile}": ${err.message}`; reject(new Error(msg)); }); readable.pipe(writable); }); } const FPS = 30; let frame = 0, tasks = [], interval = setInterval(() => tasks.push( saveCanvas(canvas, `frame_${frame++}.png`)), 1000 / FPS); // when animation is done, stop timer // and wait for images to be written clearInterval(interval); Promise.all(tasks).then(encodeVideo); function encodeVideo() { // too much code to show here, but basically run FFmpeg // externally with "-i" option containing "frame_%d.png" // and "-r" = FPS. If you want to encode to VP9 + WEBM, // definitely see: http://wiki.webmproject.org/ffmpeg/vp9-encoding-guide } 

然后使用FFmpeg将一系列图像编码成video。
对于encodeVideo()后面的代码,你可以看看这个例子 。

编辑canvas.pngStream()可能会有一个问题,当animation循环连续绘制在一个canvas上时写入不正确的帧 – 可能是每个帧需要创build一个canvas的副本? 那肯定会造成很大的记忆压力。

我认为铬无头模式可能已经支持WebGL,而且是另一种可能性。 video渲染部分尚未到来: https : //bugs.chromium.org/p/chromium/issues/detail?id = 781117