【问题标题】:Best way to record a HTML Canvas/WebGL animation server-side into a video?将 HTML Canvas/WebGL 动画服务器端录制到视频中的最佳方法?
【发布时间】:2015-10-08 14:02:45
【问题描述】:

我有一组可以在 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. Use node-canvas
这是 TJ Holowaychuk 的一个库,它允许在 Node.js 上渲染 HTML Canvas。但它有其自身的局限性,而且我还没有真正探索过这个领域。 (如果有人可以进一步了解这个库)

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

【问题讨论】:

  • node-canvas 似乎适用于二维上下文。我找到了node-webgl 包,但未能成功安装其依赖项。但是,作为评论,我必须写:如果您想记录 webgl 上下文(并将其用作渲染引擎)为自己输出视频,1×1,您可以在服务器端使用xhr on本地服务器。但是,如果您想向 Internet 上的每个连接用户输出视频,只需考虑它要求您的 serverSSS 为每个访问者配置一对 CPU-GPU。我一直在检查谷歌的“服务器端 3D 渲染”:2015 年的技术现在没什么大不了的。
  • 另外:您所描述的是使用 webgl 作为自己的渲染引擎的方式,这就是我在上面写它的原因。如果您只在网站上寻找方法,您的问题与stackoverflow.com/questions/64291/… 重复。但在所有的答案中,没有一个能带来任何东西^^
  • @Atrahasis 如果我们只想记录 Canvas 而不是 webGL。可以使用节点画布吗?是否支持视频和动画?
  • cccapture 在浏览器右侧..?

标签: node.js ffmpeg three.js phantomjs node-canvas


【解决方案1】:

我不认为 node-canvas 支持 webgl 上下文,所以你会 必须使用围绕 2d 绘图构建的库,它当然 不支持任何视频编解码器。

如果你可以使用 node-canvas 让你的动画工作,你可以 以适合您的内容的速率抓取动画帧, 像这样:

披露:我已成功使用 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()后面的代码可以看this example

编辑canvas.pngStream() 写作可能有问题 动画循环连续绘制时不正确的帧 那个画布——也许需要创建一个画布的副本 每帧?这肯定会造成很大的内存压力。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 渲染发生在哪里?我可以将刚刚渲染的场景的视频保存在文件系统上吗?
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2016-02-03
      • 2019-05-13
      • 2021-10-06
      • 1970-01-01
      • 2012-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-10
      相关资源
      最近更新 更多