我知道MediaRecorder API以及如何录制屏幕/音频/视频,然后下载这些录音.我也知道像react-media-recorder这样的npm模块利用了这个API.

我想录制一个滚动n秒的屏幕录制窗口,允许用户创建剪辑,然后能够共享这些剪辑.我无法录制整个会话,因为我不知道它们会持续多长时间,这意味着我不知道录制的内容可能会有多大(我假设录制的内容在内存中是有限制的)

是否有任何简单的方法可以使用MediaRecorder来记录滚动窗口(即始终在内存中记录最后30秒)?

推荐答案

我花了很长一段时间试图使这项工作.不幸的是,对我有效的唯一解决方案是制作30台录音机.

这个问题的简单解决方案是调用recorder.start(1000)以在1秒的时间间隔内记录数据,然后在dataavailable事件上维护一个循环缓冲区.问题是MediaRecorder支持的编码数量非常非常有限.这些编码都不允许从一开始就丢弃数据包,因为它们包含重要的元数据.随着对协议的更好理解,我相信在某种程度上,这一策略是可行的.然而,仅仅将数据包连接在一起(当一些数据包丢失时)并不能创建有效的文件.

我的另一次try 同时使用了两个MediaRecorder个对象.其中一个将记录第二个长启动数据包,另一个将记录常规数据包.在拍摄片段时,这将来自第一个记录器的起始数据包与来自第二个记录器的数据包相结合.然而,这通常会导致录制损坏.

这个解决方案并不完美,但确实有效:其思想是保持30 MediaRecorder个对象,每个对象偏移1秒.在本演示中,剪辑的长度为5秒,而不是30秒:

<canvas></canvas><button>Clip!</button>

<style>
   canvas, video, button {
      display: block;
   }
</style>

<!-- draw to the canvas to create a stream for testing -->
<script>
   const canvas = document.querySelector('canvas');
   const ctx = canvas.getContext('2d');
   // fill background with white
   ctx.fillStyle = 'white';
   ctx.fillRect(0, 0, canvas.width, canvas.height);

   // randomly draw stuff
   setInterval(() => {
      const x = Math.floor(Math.random() * canvas.width);
      const y = Math.floor(Math.random() * canvas.height);
      const radius = Math.floor(Math.random() * 30);
      ctx.beginPath();
      ctx.arc(x, y, radius, 0, Math.PI * 2);
      ctx.stroke();
   }, 100);
</script>

<!-- actual recording -->
<script>
   // five second clips
   const LENGTH = 5;

   const codec = 'video/webm;codecs=vp8,opus'
   const stream = canvas.captureStream();

   // circular buffer of recorders
   let head = 0;
   const recorders = new Array(LENGTH)
      .fill()
      .map(() => new MediaRecorder(stream, { mimeType: codec }));

   // start them all
   recorders.forEach((recorder) => recorder.start());

   let data = undefined;
   recorders.forEach((r) => r.addEventListener('dataavailable', (e) => {
      data = e.data;
   }));

   setInterval(() => {
      recorders[head].stop();
      recorders[head].start();

      head = (head + 1) % LENGTH;
   }, 1000);

   // download the data
   const download = () => {
      if (data === undefined) return;
      const url = URL.createObjectURL(data);

      // download the url
      const a = document.createElement('a');
      a.download = 'test.webm';
      a.href = url;
      a.click();
      URL.revokeObjectURL(url);
   };

   // stackoverflow doesn't allow downloads
   // we show the clip instead
   const show = () => {
      if (data === undefined) return;
      const url = URL.createObjectURL(data);

      // display url in new video element
      const v = document.createElement('video');
      v.src = url;
      v.controls = true;
      document.body.appendChild(v);
   };

   document.querySelector('button').addEventListener('click', show);
</script>

Javascript相关问答推荐

如何在ReactJS中修复这种不需要的按钮行为?

如何比较嵌套对象的更改并创建更改报告

更新Reduxstore 中的状态变量,导致整个应用程序出现不必要的重新渲染

如何解决这个未能在响应上执行json:body stream已读问题?

可以的.是否可以在不预编译的情况下使用嵌套 Select 器?

ReactJS中的material UI自动完成类别

从实时数据库(Firebase)上的子类别读取数据

提交表格后保留Web表格中的收件箱值?

在Angular中将样式应用于innerHTML

类型自定义lazy Promise. all

使搜索栏更改语言

如何在每次单击按钮时重新加载HighChart/设置HighChart动画?

WhatsApp Cloud API上载问题:由于MIME类型不正确而导致接收&Quot;INVALID_REQUEST";错误

自定义图表工具提示以仅显示Y值

MongoDB受困于太多的数据

删除加载页面时不存在的元素(JavaScript)

如果没有页面重新加载Angular ,innerHTML属性绑定不会更新

在Java脚本中录制视频后看不到曲目

P5.js中矩形内的圆弧

输入数据覆盖JSON文件