网络摄像头录制是一项有用的应用程序功能,因为它适用于视频会议、内容创作、安全(如生物识别认证)以及监控目的。
在本文中,将学习如何实现网络摄像头录制和保存视频。
先决条件
- 基本了解 HTML、CSS 和 JavaScript
- 熟悉现代 Web 开发工具
- 开发环境(例如 VS Code、浏览器)
设置项目
创建基本的 HTML 结构
-
在代码编辑器并创建一个 HTML5 模板
-
添加必要的元素
-
- 启动摄像头、开始录制和停止录制的按钮
- 选择你视频和音频的下拉菜单
- 录制后下载视频的链接
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>网络摄像头录制</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<main class="main">
<div>
<h1>网络摄像头录制</h1>
<div class="webcam-container">
<div class="red-dot"></div>
<video id="webcam" autoplay muted></video>
<div><button id="start-camera">启动摄像头</button></div>
<div class="media-devices">
<select name="video-devices" id="video-devices" class="video-devices">
<option>选择视频设备</option>
</select>
<select name="audio-devices" id="audio-devices" class="audio-devices">
<option>选择音频设备</option>
</select>
</div>
<div>
<button id="start-record" disabled>开始录制</button>
<button id="stop-record" disabled>停止录制</button>
</div>
<a id="download-link" style="display: none">下载录制内容</a>
</div>
</div>
</main>
<script src="./script.js"></script>
</body>
</html>
样式
- 在 CSS 文件中,添加样式
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
width: 100%;
height: 100%;
}
.main {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 80px 0;
}
.main > div {
display: flex;
flex-direction: column;
gap: 20px;
}
.main > div > h1 {
text-align: center;
}
.webcam-container {
min-height: 500px;
max-width: 340px;
width: 100%;
border: 1px solid #cccccc;
padding: 16px;
display: flex;
flex-direction: column;
gap: 20px;
position: relative;
}
.media-devices {
display: flex;
flex-direction: column;
}
.red-dot {
height: 10px;
width: 10px;
border-radius: 99999px;
z-index: 1;
background-color: red;
position: absolute;
right: 28px;
top: 28px;
visibility: hidden;
}
.show {
visibility: visible;
}
.red-border {
border: 2px solid red;
}
video {
height: 80%;
width: 100%;
border: 1px solid #aaaaaa;
object-fit: fill;
}
select {
padding: 8px;
width: 100%;
}
button {
padding: 8px 12px;
border: 1px solid black;
background-color: rgba(0, 0, 0, 0.8);
color: white;
font-size: 14px;
font-weight: 500;
cursor: pointer;
width: 100%;
}
button:hover {
background-color: rgba(0, 0, 0, 1);
}
button:disabled {
background-color: rgba(0, 0, 0, 0.5);
}
.webcam-container > div {
display: flex;
align-items: center;
gap: 12px;
}
@media screen and (min-width: 768px) {
.webcam-container {
max-width: 700px;
}
.media-devices {
display: flex;
flex-direction: row;
}
}
MediaRecorder 和 MediaDevices
MediaRecorder 是 MediaStream Recording API 的一个接口,它允许你使用其 MediaRecorder() 构造函数录制媒体。你可以使用提供的构造函数创建一个 MediaRecorder 实例,并将其保存在一个变量中。该实例将接收来自 MediaDevices.getUserMedia() 方法的流。你可以访问 start() 和 stop() 方法,分别用于开始和停止录制会话。
MediaDevices 是 Media Capture and Streams API 的一个接口,它允许用户访问连接的媒体设备,如摄像头和麦克风。它提供了 5 个方法和一个事件。然而,你只需要 2 个方法;getUserMedia() 用于访问用户的媒体设备并返回媒体流,enumerateDevices() 用于获取连接到系统的媒体设备选项数组。
实现录制功能
变量声明和初始设置
const videoElement = document.querySelector("#webcam");
const startButton = document.querySelector("#start-record");
const startCameraBtn = document.querySelector("#start-camera");
const stopButton = document.querySelector("#stop-record");
const downloadLink = document.querySelector("#download-link");
const videoSelect = document.querySelector("#video-devices");
const audioSelect = document.querySelector("#audio-devices");
const redDot = document.querySelector(".red-dot");
let mediaRecorder;
let recordedChunks = [];
let currentStream = null;
这段代码初始化了实现网络摄像头录制功能所需的 DOM 元素和变量。
媒体设备
async function populateDevices() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
// 清空并填充视频设备
videoSelect.innerHTML = devices
.filter((device) => device.kind === "videoinput")
.map(
(device) =>
`<option value="${device.deviceId}">${
device.label || `视频设备 ${videoSelect.options.length + 1}`
}</option>`
)
.join("");
// 清空并填充音频设备
audioSelect.innerHTML = devices
.filter((device) => device.kind === "audioinput")
.map(
(device) =>
`<option value="${device.deviceId}">${
device.label || `音频设备 ${audioSelect.options.length + 1}`
}</option>`
)
.join("");
} catch (error) {
console.error("访问设备时出错:", error);
}
}
这个函数检索并填充可用的视频和音频输入设备到相应的下拉菜单中。
解释
navigator.mediaDevices.enumerateDevices():检索可用的媒体输入和输出设备。filter和map:过滤并映射设备以创建下拉菜单的<option>元素。device.label:如果可用,使用设备标签,否则提供默认标签。
获取媒体流
async function getMedia(constraints) {
try {
if (currentStream) {
stopTracks(currentStream);
}
currentStream = await navigator.mediaDevices.getUserMedia(constraints);
videoElement.srcObject = currentStream;
startButton.disabled = false;
startCameraBtn.disabled = true;
} catch (error) {
console.error("访问媒体时出错:", error);
}
}
从用户的网络摄像头和麦克风请求媒体流。
解释
navigator.mediaDevices.getUserMedia(constraints):请求媒体流。videoElement.srcObject = currentStream:在<video>元素中显示流。stopTracks(currentStream):如果存在当前流,停止它。
停止媒体轨道
function stopTracks(stream) {
stream.getTracks().forEach((track) => track.stop());
}
停止媒体流。
解释
stream.getTracks():检索流中的所有轨道。track.stop():停止每个轨道。
设备处理器
videoSelect.addEventListener("change", async () => {
await getMedia({
video: { deviceId: videoSelect.value },
audio: { deviceId: audioSelect.value },
});
});
audioSelect.addEventListener("change", async () => {
await getMedia({
video: { deviceId: videoSelect.value },
audio: { deviceId: audioSelect.value },
});
});
监听器视频和音频下拉菜单的变化。
解释
- 当用户选择不同的设备时,调用
getMedia函数并使用新的约束条件。
初始设备设置
startCameraBtn.addEventListener("click", async () => {
await getMedia({
video: { deviceId: videoSelect.value },
audio: { deviceId: audioSelect.value },
});
populateDevices();
});
点击“启动摄像头”按钮时启动网络摄像头。
解释
- 调用
getMedia启动网络摄像头。 - 调用
populateDevices刷新设备下拉菜单。
录制控制
startButton.addEventListener("click", () => {
recordedChunks = [];
mediaRecorder = new MediaRecorder(currentStream, {
mimeType: "video/webm; codecs=vp9",
});
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) recordedChunks.push(event.data);
};
mediaRecorder.start();
redDot.classList.add("show");
videoElement.classList.add("red-border");
startButton.disabled = true;
stopButton.disabled = false;
});
点击“开始录制”按钮时启动录制。
解释
MediaRecorder(currentStream, { mimeType: "video/webm; codecs=vp9" }):使用当前流和指定的 MIME 类型创建MediaRecorder实例。mediaRecorder.ondataavailable:事件监听器用于收集录制的数据块。mediaRecorder.start():开始录制。- 更新视觉指示器(
redDot和videoElement边框)以显示录制处于活动状态。
stopButton.addEventListener("click", () => {
if (mediaRecorder?.state === "recording") {
mediaRecorder.stop();
redDot.classList.remove("show");
videoElement.classList.remove("red-border");
stopButton.disabled = true;
startButton.disabled = false;
}
mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: "video/webm" });
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = `recording-${Date.now()}.webm`;
downloadLink.style.display = "block";
// 清理
recordedChunks = [];
};
});
点击“停止录制”按钮时停止录制。
解释
mediaRecorder.stop():停止录制。- 更新视觉指示器以显示录制已停止。
mediaRecorder.onstop:事件监听器用于处理录制结束,从录制块创建 Blob 并生成可下载链接。
窗口关闭时清理
window.addEventListener("beforeunload", () => {
if (currentStream) {
stopTracks(currentStream);
}
});
确保在关闭窗口时停止媒体流。
解释
stopTracks(currentStream):停止当前流中的所有轨道。
结论
本文提供了一个完整的实现方案,使用 JavaScript 在网页上实现网络摄像头录制。它涵盖了设备选择、流管理、录制控制和清理,确保用户获得流畅且功能齐全的体验。
你可以通过添加暂停和播放功能以及实现这些事件的其他功能,将项目进一步扩展。
应用:webcam-recording.vercel.app/
GitHub 代码:github.com/DrPrime01/w…