我知道MediaRecorder API
以及如何录制屏幕/音频/视频,然后下载这些录音.我也知道像react-media-recorder
这样的npm模块利用了这个API.
我想录制一个滚动n秒的屏幕录制窗口,允许用户创建剪辑,然后能够共享这些剪辑.我无法录制整个会话,因为我不知道它们会持续多长时间,这意味着我不知道录制的内容可能会有多大(我假设录制的内容在内存中是有限制的)
是否有任何简单的方法可以使用MediaRecorder
来记录滚动窗口(即始终在内存中记录最后30秒)?
我知道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>