
本文详解如何修复 web 应用中摄像头无法启动的问题,重点纠正 `navigator.mediadevices.getusermedia()` 的错误调用方式,并完整集成 face-api.js 实现基于浏览器的人脸检测、关键点定位与表情识别。
在浏览器中接入摄像头并实现实时 AI 分析(如人脸检测),是前端计算机视觉项目的关键第一步。你当前代码的核心问题在于 getUserMedia() 的调用方式——它不接受回调函数作为第二、第三个参数,而是返回一个 Promise,应使用 .then() 和 .catch() 处理成功与失败逻辑。旧式“success/failure 回调”写法已废弃,且在现代浏览器(尤其是 Chrome 73+)中会导致静默失败或 NotAllowedError / NotFoundError,这正是你尝试多种设备和权限后仍无视频流的原因。
✅ 正确的初始化方式如下:
const video = document.getElementById('video');
// ✅ 正确:返回 Promise,链式处理
navigator.mediaDevices.getUserMedia({
video: {
width: { ideal: 1280 },
height: { ideal: 720 }
}
})
.then(stream => {
video.srcObject = stream;
video.play(); // 显式调用 play() 提升兼容性
})
.catch(err => {
console.error('❌ 摄像头访问被拒绝或不可用:', err.name, err.message);
alert('请检查摄像头权限、硬件连接,或尝试在 HTTPS 环境下运行(本地 localhost 除外)');
});⚠️ 注意事项:
- 必须在安全上下文运行:getUserMedia() 要求页面处于 https:// 或 http://localhost(含 127.0.0.1)。Live Server 在 VS Code 中默认满足此条件,但若通过 file:// 直接打开 HTML 将必然失败。
- :尤其在移动端或新版 Chrome 中,未静音的自动播放会被阻止;muted 是必要属性。
- 模型加载与视频就绪需解耦:你原代码中将模型加载 Promise.all(...).then(startVideo) 与 video.addEventListener('play', ...) 混用,易导致 canvas 初始化早于视频尺寸就绪。推荐改为监听 loadeddata 或显式等待 video.readyState === 4。
? 完整可运行的优化结构示例(整合 face-api.js):
// script.js
const video = document.getElementById('video');
let canvas, ctx, displaySize;
// 1. 先加载模型(异步)
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri('/models'),
faceapi.nets.faceLandmark68Net.loadFromUri('/models'),
faceapi.nets.faceExpressionNet.loadFromUri('/models')
]).then(() => {
// 2. 再启动摄像头
return navigator.mediaDevices.getUserMedia({ video: true });
}).then(stream => {
video.srcObject = stream;
return new Promise(resolve => {
video.onloadeddata = () => {
resolve();
startDetection(); // 视频就绪后启动检测循环
};
});
}).catch(err => console.error('模型或摄像头加载失败:', err));
function startDetection() {
canvas = faceapi.createCanvasFromMedia(video);
document.body.append(canvas);
// 同步 canvas 尺寸与 video 实际渲染尺寸(非 HTML 属性值)
const videoRect = video.getBoundingClientRect();
displaySize = { width: videoRect.width, height: videoRect.height };
faceapi.matchDimensions(canvas, displaySize);
// 检测循环
setInterval(async () => {
if (!video || video.readyState !== 4) return; // 防止未就绪时调用
const detections = await faceapi
.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions())
.withFaceLandmarks()
.withFaceExpressions();
const resizedDetections = faceapi.resizeResults(detections, displaySize);
ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
faceapi.draw.drawDetections(canvas, resizedDetections);
faceapi.draw.drawFaceLandmarks(canvas, resizedDetections);
faceapi.draw.drawFaceExpressions(canvas, resizedDetections);
}, 100);
}? 最后提醒:
- 确保 /models/ 目录下包含所有 .json 及二进制权重文件(如 face_landmark_68_model-weights.bin),路径大小写敏感;
- 若仍黑屏,请打开浏览器开发者工具 → Application → Clear storage → 清除权限缓存,再重新授予权限;
- 生产环境务必使用 HTTPS —— HTTP 页面(非 localhost)将彻底禁用摄像头 API。
至此,你的浏览器将稳定拉起摄像头,并流畅运行 face-api.js 的实时人脸分析流水线。










