web 提供MediaRecorder 类来实现前端录屏功能。编码过程由浏览器实现,依赖浏览器的能力,因为该标准由 w3c 推进,目前主要的试验田在 chrome 和 firefox,移动端兼容安卓内置的 chrome 内核浏览器。
使用的时候,先调用构造函数,传入MediaStream
媒体流,实现摄像头屏幕录制
MediaRecorder
常用的方法
//该事件可用于获取录制的媒体资源
recorder.ondataavailable = (e) => {
if (showDataAvailable) {
console.log("trigger ondataavailable");
}
allChunks.push(e.data);
};
//开始录制
recorder.start();
//停止录制
recorder.stop();
//暂停录制
recorder.pause();
检测浏览器编码能力的 api
MediaRecorder.isTypeSupported(format);
可以把下面这段代码贴进 console,来测试当前浏览器的支持状况。
var types = [
"video/webm",
"audio/webm",
"video/webm;codecs=vp8",
"video/webm;codecs=daala",
"video/webm;codecs=h264",
"audio/webm;codecs=opus",
"video/mpeg",
];
for (var i in types) {
console.log(
"Is " +
types[i] +
" supported? " +
(MediaRecorder.isTypeSupported(types[i]) ? "Maybe!" : "Nope :(")
);
}
屏幕录制示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<video></video>
<button class="record-btn">录制</button>
<button class="record-stop-btn">停止</button>
<button class="record-pause-btn">暂停</button>
<button class="record-resume-btn">恢复录制</button>
</body>
<script>
let btn = document.querySelectorAll(".record-btn")[0];
console.log("btn: ", btn);
let btnStop = document.querySelectorAll(".record-stop-btn")[0];
let btnPause = document.querySelectorAll(".record-pause-btn")[0];
let btnResume = document.querySelectorAll(".record-resume-btn")[0];
let mediaRecorder = null;
btn.addEventListener("click", async (e) => {
let stream = await navigator.mediaDevices.getDisplayMedia({
video: true,
});
const mime = MediaRecorder.isTypeSupported("video/webm;codecs=vp9")
? "video/webm;codecs=vp9"
: "video/webm";
mediaRecorder = new MediaRecorder(stream, { mimeType: mime });
let chunks = [];
mediaRecorder.addEventListener("dataavailable", (e) => {
console.log("dataavailable");
chunks.push(e.data);
});
mediaRecorder.addEventListener("stop", (e) => {
let blob = new Blob(chunks, {
type: chunks[0].type,
});
let url = URL.createObjectURL(blob);
let video = document.querySelector("video");
video.src = url;
let a = document.createElement("a");
a.href = url;
a.download = "video.webm";
a.click();
});
mediaRecorder.start();
});
btnStop.addEventListener("click", function () {
mediaRecorder.stop();
});
btnPause.addEventListener("click", function () {
mediaRecorder.pause();
});
btnResume.addEventListener("click", function () {
mediaRecorder.resume();
});
</script>
</html>
调起摄像头录制,多了一步从摄像头中获取视频,放入 canvas 中渲染的过程。
let allChunks = [];
let format = "video/webm;codecs=vp9";
const stream = canvas.captureStream(60); // 录制帧率60fps
const recorder = new MediaRecorder(stream, {
mimeType: format,
});
recorder.ondataavailable = (e) => {
allChunks.push(e.data);
};
调起摄像头示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>MediaRecorder使用示例</title>
</head>
<style>
canvas {
box-shadow: 0 0 10px gray;
display: block;
}
</style>
<h3 style="text-align: center; margin-top: 10px">
MediaRecorder使用示例 - 摄像头版本
</h3>
<p style="text-align: center; margin-top: 10px; color: grey">
点击画布在视频上作画<button id="clearBtn">清除笔迹</button>
</p>
<div style="text-align: center; margin-top: 10px">
<canvas
id="canvas"
height="460"
width="640"
style="width: 640px; margin: auto"
></canvas>
<video src="" id="srcvideo" style="display: none"></video>
</div>
<div style="text-align: center; margin-top: 10px">
<button id="startBtn" disabled>开始录制</button>
<button id="pauseBtn" disabled>暂停录制</button>
<button id="resumeBtn" disabled>恢复录制</button>
<button id="stopBtn" disabled>结束录制</button>
</div>
<div style="text-align: center; margin-top: 20px">
<p>切换录制编码格式</p>
<p>
<input
type="radio"
name="format"
value="video/webm;codecs=vp8"
onclick="setFormatSelect('video/webm;codecs=vp8')"
/>video/webm;codecs=vp8
</p>
<p>
<input
type="radio"
name="format"
value="video/webm;codecs=vp9"
onclick="setFormatSelect('video/webm;codecs=vp9')"
checked="checked"
/>video/webm;codecs=vp9
</p>
<p>
<input
type="radio"
name="format"
value="video/webm;codecs=h264"
onclick="setFormatSelect('video/webm;codecs=h264')"
/>video/webm;codecs=h264
</p>
<p>
<input
type="radio"
name="format"
value="video/webm;codecs=avc1"
onclick="setFormatSelect('video/webm;codecs=avc1')"
/>video/webm;codecs=avc1
</p>
<p>
<input
type="radio"
name="format"
value="video/x-matroska;codecs=avc1"
onclick="setFormatSelect('video/x-matroska;codecs=avc1')"
/>video/x-matroska;codecs=avc1
</p>
</div>
<script>
var allChunks = [];
var mousex = 0;
var mousey = 0;
var drawArray = [];
init();
function init() {
const ctx = canvas.getContext("2d");
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
navigator.mediaDevices
.getUserMedia({
video: true,
})
.then(function (mediaStream) {
var srcvideo = document.getElementById("srcvideo");
srcvideo.srcObject = mediaStream;
srcvideo.play();
playCanvas(srcvideo, ctx);
});
// document.body.onmousedown = e => {
// }
clearBtn.onclick = (e) => {
drawArray = [];
};
canvas.onmousemove = (e) => {
const { top, left } = canvas.getBoundingClientRect();
mousex = e.clientX - left;
mousey = e.clientY - top;
};
canvas.onmousedown = (e) => {
const { top, left } = canvas.getBoundingClientRect();
var downx = e.clientX - left;
var downy = e.clientY - top;
drawArray.push({
x: downx,
y: downy,
});
};
setRecorder();
setFormatSelect("video/webm;codecs=vp9");
}
function playCanvas(srcvideo, ctx) {
ctx.drawImage(srcvideo, 0, 0, 640, 460);
for (var i = 0; i < drawArray.length; i++) {
ctx.beginPath();
const xFraction = drawArray[i].x / 640;
const yFraction = drawArray[i].y / 460;
const r = 255 * (1 - xFraction);
const g = 255 * yFraction;
const b = 255 * xFraction * (1 - yFraction);
ctx.fillStyle = `rgba(${r | 0}, ${g | 0}, ${b | 0}, 1)`;
ctx.arc(drawArray[i].x, drawArray[i].y, 10, 0, 2 * Math.PI);
ctx.fill();
}
requestAnimationFrame(() => {
playCanvas(srcvideo, ctx);
});
}
function setFormatSelect(format) {
if (!MediaRecorder.isTypeSupported(format)) {
alert(format);
alert("当前浏览器不支持该编码类型");
return;
}
allChunks = [];
setRecorder(format);
}
// 使用API录制Canvas核心代码如下
function setRecorder(format) {
const stream = canvas.captureStream(60); // 60 FPS recording
const recorder = new MediaRecorder(stream, {
mimeType: format,
});
recorder.ondataavailable = (e) => {
allChunks.push(e.data);
};
startBtn.disabled = false;
startBtn.onclick = (e) => {
recorder.start(10);
startBtn.disabled = true;
pauseBtn.disabled = false;
resumeBtn.disabled = true;
stopBtn.disabled = false;
};
stopBtn.onclick = (e) => {
recorder.stop();
blobDownload(format);
startBtn.disabled = false;
pauseBtn.disabled = true;
resumeBtn.disabled = true;
stopBtn.disabled = true;
};
pauseBtn.onclick = (e) => {
recorder.pause();
pauseBtn.disabled = true;
resumeBtn.disabled = false;
};
resumeBtn.onclick = (e) => {
recorder.resume();
pauseBtn.disabled = false;
resumeBtn.disabled = true;
};
}
//下载录制的视频
function blobDownload(format) {
const link = document.createElement("a");
link.style.display = "none";
const fullBlob = new Blob(allChunks);
const downloadUrl = window.URL.createObjectURL(fullBlob);
link.href = downloadUrl;
link.download = "media - " + format + ".mp4";
document.body.appendChild(link);
link.click();
link.remove();
}
</script>
</html>
如果chrome 报错 navigator.mediaDevices is undefined
解决方法
-
chrome 下
打开 chrome://flags/#unsafely-treat-insecure-origin-as-secure -
选 enabled
填写需要调试的 URL,多个 URL 以,隔开
完全重启 chrome 后起效(改了之后下面也会有个 relaunch 按钮) -
重启后发现,就可以非 https 调试了
-
edge 下 毕竟现在 edge 和 chrome 内核一样了,所以操作方法一样,只是打开链接不同:edge://flags/#unsafely-treat-insecure-origin-as-secure