17

我有一组可以在 Canvas (fabric.js) 或 WebGL (three.js) 中制作的动画。我需要通过脚本在服务器端自动记录它们并输出视频文件。

动画包括:

  1. 图片
  2. 视频(带音频)
  3. 其他动画/效果

在过去的几个月里,我对此进行了很多研究。

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

2.使用 Websockets 使用 DataURL 将数据发送回服务器
这里再次,我们需要在浏览器上运行动画(我们不能,因为我们必须这样做服务器上的一切)。

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

如果有人以前做过或可以指导我到有用的地方。
我们需要做的就是使用一些数据来创建动画并将其录制到视频中,一切都在服务器端。

4

3 回答 3

3

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

于 2016-12-14T13:48:45.447 回答
2

我认为 node-canvas 不支持 webgl 上下文,因此您必须使用围绕 2d 绘图构建的库,并且它肯定不支持任何视频编解码器。

如果您可以使用节点画布使动画正常工作,则可以以适合您的内容的速率抓取动画帧,如下所示:

披露:我已经成功地使用 FFmpeg 对一系列外部生成的图像进行编码,但还没有尝试过下面的 setInterval() 方法。除了动画开销本身之外,我不知道以 30 FPS 的速度将画布导出为 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 将一系列图像编码成视频。
后面的代码encodeVideo()可以看这个例子

编辑:当动画循环不断地在那个画布上绘制时,写入不正确的帧可能会出现问题canvas.pngStream()——也许每帧都需要创建一个画布的副本?这肯定会造成巨大的记忆压力。

于 2016-10-07T02:52:06.690 回答
0

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

于 2017-11-14T17:16:52.067 回答