为什么写这篇
网上关于 WebRTC 的教程大多只讲「摄像头预览」或「纯录音」,很少把「实时画面 + 实时麦克风」同时跑通,还附送「抓拍 + 录制 + 下载」。今天我把踩坑后的最简可运行代码整理成一篇,5 分钟带你复现。
一、技术要点拆解
功能 | 关键 API | 注意点 |
---|---|---|
获取音视频流 | navigator.mediaDevices.getUserMedia(constraints) |
必须在 HTTPS 或 localhost;首次需用户授权 |
实时预览 | <video autoplay playsinline> + video.srcObject = stream |
移动端必须加 playsinline ,否则全屏 |
麦克风耳返 | 把 <video muted> 去掉即可 |
Chrome 默认回声消除,不会啸叫 |
拍照 | canvas.drawImage(video, 0, 0) + canvas.toDataURL() |
生成 JPEG/PNG 直接展示 |
录制 | MediaRecorder |
兼容性写法:先检查 mimeType |
下载 | URL.createObjectURL(blob) + <a download> |
记得 revokeObjectURL 避免内存泄漏 |
二、代码架构
ini
index.html
├── <video id="preview"> // 实时预览
├── <button id="snapBtn"> // 抓拍
├── <canvas id="canvas"> // 抓拍中转
├── <img id="photo"> // 抓拍结果
├── <button id="recBtn"> // 开始录制
├── <button id="stopBtn"> // 停止录制
├── <video id="recorded"> // 回放
└── <a id="downLink"> // 下载
三、完整代码(复制即跑)
⚠️ 保存为 index.html
,通过 HTTPS 或 localhost 打开!
ini
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>H5 摄像头/麦克风快速测试</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
body{margin:0;font-family:Arial;background:#111;color:#fff}
video{width:100%;max-width:480px;background:#000}
button{margin:6px;padding:8px 14px;font-size:15px}
.box{max-width:480px;margin:auto;text-align:center}
</style>
</head>
<body>
<div class="box">
<h3>摄像头 / 麦克风测试</h3>
<!-- 实时预览 -->
<video id="preview" autoplay playsinline muted></video>
<!-- 抓拍 -->
<br>
<button id="snapBtn">抓拍照片</button>
<canvas id="canvas" style="display:none"></canvas>
<img id="photo" style="max-width:100%;margin-top:6px;display:none">
<!-- 录制 -->
<br>
<button id="recBtn">开始录制</button>
<button id="stopBtn" disabled>停止录制</button>
<br>
<video id="recorded" controls style="margin-top:6px;display:none"></video>
<br>
<a id="downLink" style="display:none;color:#0f0">下载</a>
</div>
<script>
/************ 1. 实时预览 ************/
const preview = document.getElementById('preview');
const constraints = { video: { facingMode: 'user' }, audio: true };
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
preview.srcObject = stream;
window.stream = stream; // 后面录制复用
})
.catch(err => alert('获取摄像头/麦克风失败:' + err));
/************ 2. 抓拍 ************/
document.getElementById('snapBtn').addEventListener('click', () => {
const canvas = document.getElementById('canvas');
const video = preview;
canvas.width = video.videoWidth;
canvas.height= video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
const dataURL = canvas.toDataURL('image/jpeg');
const photo = document.getElementById('photo');
photo.src = dataURL;
photo.style.display = 'inline-block';
});
/************ 3. 录制 ************/
let mediaRecorder;
let recordedBlobs = [];
const recBtn = document.getElementById('recBtn');
const stopBtn = document.getElementById('stopBtn');
const recorded = document.getElementById('recorded');
const downLink = document.getElementById('downLink');
recBtn.onclick = () => {
recorded.style.display = 'none';
downLink.style.display = 'none';
recordedBlobs = [];
const options = { mimeType: 'video/webm;codecs=vp9,opus' };
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
alert('浏览器不支持 webm/opus,将尝试默认格式');
options.mimeType = undefined;
}
mediaRecorder = new MediaRecorder(window.stream, options);
mediaRecorder.ondataavailable = e => {
if (e.data && e.data.size > 0) recordedBlobs.push(e.data);
};
mediaRecorder.onstop = () => {
const blob = new Blob(recordedBlobs, { type: 'video/webm' });
recorded.src = URL.createObjectURL(blob);
recorded.style.display = 'inline-block';
downLink.href = recorded.src;
downLink.download = 'test.webm';
downLink.style.display = 'inline-block';
};
mediaRecorder.start();
recBtn.disabled = true;
stopBtn.disabled = false;
};
stopBtn.onclick = () => {
mediaRecorder.stop();
recBtn.disabled = false;
stopBtn.disabled = true;
};
</script>
</body>
</html>
四、常见问题 FAQ
- 本地双击无法调用设备?
必须使用 HTTPS 或localhost
。最简单的本地服务器:npx serve .
- iOS 不能播放?
确保<video>
带playsinline
属性,且 iOS 14+ 才支持MediaRecorder
。 - 下载的文件没有声音?
检查constraints
里是否加了audio: true
;录制时mimeType
要包含opus
。
五、下一步可以玩什么?
- 把
getUserMedia
换成getDisplayMedia
做屏幕录制 - 接入 WebRTC 做 1 对 1 视频通话
- 用 Canvas 2D + WebGL 做美颜、滤镜