在p5.js中导出视频

13

我正在使用p5.js创建一个简单的动画程序。当用户点击保存按钮时,我想要下载动画的视频。

我有一个名为frames的对象,其中每个键都标记为frame_1frame_2等等。与每个键相关联的值是组成该帧的线段的array

我尝试思考一种方法来获取这些数据并创建一个mp4视频。p5.js具有内置的save函数,我认为它可能会有所帮助,但它本身并不是一个完整的解决方案。我可以将每个帧保存为单独的图像,然后在客户端上以某种方式将这些图像拼接在一起,但我还没有找到解决这个问题的方法。

任何其他方法也都可以。唯一的要求是必须在客户端完成。


1
发送图像流数据到ffmpeg不是一个选项吗?如果可以的话,将画布帧转换为动画gif会更加方便。 - Ayush Seth
@MasterYushi 目前来说,GIF 是一个非常好的替代方案。我之前没有想到过。 - Siunami
3个回答

23
由于p5.js基于Canvas API构建,在现代浏览器中,您可以使用MediaRecorder完成此工作。

const btn = document.querySelector('button'),
  chunks = [];

function record() {
  chunks.length = 0;
  let stream = document.querySelector('canvas').captureStream(30),
    recorder = new MediaRecorder(stream);
  recorder.ondataavailable = e => {
    if (e.data.size) {
      chunks.push(e.data);
    }
  };
  recorder.onstop = exportVideo;
  btn.onclick = e => {
    recorder.stop();
    btn.textContent = 'start recording';
    btn.onclick = record;
  };
  recorder.start();
  btn.textContent = 'stop recording';
}

function exportVideo(e) {
  var blob = new Blob(chunks);
  var vid = document.createElement('video');
  vid.id = 'recorded'
  vid.controls = true;
  vid.src = URL.createObjectURL(blob);
  document.body.appendChild(vid);
  vid.play();
}
btn.onclick = record;

// taken from pr.js docs
var x, y;

function setup() {
  createCanvas(300, 200);
  // Starts in the middle
  x = width / 2;
  y = height;
}

function draw() {
  background(200);

  // Draw a circle
  stroke(50);
  fill(100);
  ellipse(x, y, 24, 24);

  // Jiggling randomly on the horizontal axis
  x = x + random(-1, 1);
  // Moving up at a constant speed
  y = y - 1;

  // Reset to the bottom
  if (y < 0) {
    y = height;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.7/p5.min.js"></script>
<button>start recording</button><br>


这个真的很好用。但我想知道为什么我不能立即下载视频?看起来我需要等待它...创建?加载? - dawn

6

ccapture 能很好地与 p5.js 协同工作,实现记录画布所显示内容的目标。

这里有一个 演示,展示了 ccapture 和 p5.js 的配合效果。演示源代码也在其中。

这种方法不会产生视频卡顿,因为它并非记录屏幕上所看到的内容,那可能会卡顿。相反,它将每一帧写入视频,并告诉视频以固定的帧率播放。因此,即使计算一帧需要几秒钟的时间,输出的视频也能流畅播放,不会在帧之间显示任何延迟。

然而,这种方法只适用于 Chrome 浏览器。


很高兴有一个p5编辑器,但是代码不再起作用了,webm代码出了问题。 - lesolorzanov
@lesolorzanov 感谢您的尝试。我刚刚尝试过(2020年4月18日)。它适用于Chrome浏览器。该方法仅适用于Chrome浏览器。您使用的是Chrome吗? - ffmaer
是的,我正在使用Chrome浏览器。我正在使用自己的项目,该项目具有与您相同的结构和文件,但是一旦我按下“开始录制”按钮,动画就会冻结,并且在webm-writer和ccapture中出现了很多问题。 - lesolorzanov
@lesolorzanov 抱歉,似乎出现了一些问题。我刚刚简化了代码,希望现在能够正常工作。 - ffmaer
我最终在Blender中完成了我的动画,哈哈,但还是谢谢,我会看一下的。 - lesolorzanov

3

根据你在评论中的要求,gif也可以使用,这里提供一种解决方案:

以下是一个示例p5草图,记录画布动画并将其转换为gif,使用gif.js

适用于支持Web Workers、文件API和Typed Arrays的浏览器。

我提供了这个代码,以便你可以了解如何使用这个库,因为它并没有提供太多的文档,我自己也花费了很大的精力来弄清楚它的用法。

var cnv;

var gif, recording = false;

function setup() {
    cnv = createCanvas(400, 400);

    var start_rec = createButton("Start Recording");
    start_rec.mousePressed(saveVid);

    var stop_rec = createButton("Stop Recording");
    stop_rec.mousePressed(saveVid);

    start_rec.position(500, 500);
    stop_rec.position(650, 500);

    setupGIF();
}

function saveVid() {
    recording = !recording;
    if (!recording) {
        gif.render();
    }
}
var x = 0;
var y = 0;

function draw() {
    background(51);
    fill(255);
    ellipse(x, y, 20, 20);
    x++;
    y++;

    if (recording) {
        gif.addFrame(cnv.elt, {
            delay: 1,
            copy: true
        });
    }
}

function setupGIF() {
    gif = new GIF({
        workers: 5,
        quality: 20
    });
    gif.on('finished', function(blob) {
        window.open(URL.createObjectURL(blob));
    });
}

更多信息:

当你点击 start_rec 时,这个草图开始记录帧,并在你点击 stop_rec 时停止。在你的草图中,你可能想以不同的方式控制它,但请记住,addFrame 只向 gif 添加一个帧,因此您需要在 draw 函数中调用它以添加多个帧,您可以传递一个 ImageElement、一个 CanvasElement 或一个 CanvasContext 以及其他可选参数。

gif.on 函数中,您可以指定一个回调函数来执行您喜欢的任何操作。

如果您想微调 gif 的设置,如 qualityrepeatbackground,您可以在这里阅读更多内容:这里。希望这可以帮助到您!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接