web前端通过video+canvas+MediaDevices.getUserMedia()的方式调用本地多媒体设备(不局限于摄像头)时。存在一个安全问题,为了用户的隐私安全,http协议无法使用多媒体设备。
https://blog.csdn.net/weixin_45408862/article/details/107865462
因为像摄像头和麦克风属于可能涉及重大隐私问题的API,getUserMedia()的规范提出了浏览器必须满足一系列隐私和安全要求。这个方法功能很强大,只能在安全的上下文中使用,在不安全的环境中为undefined。
安全上下文:
- HTTPS、
- file:///url方案加载的页面,
- 开发者本地测试使用localhost/127.0.0.1
在上面这些情况下才能使用多媒体设备。
想要通过ip访问多媒体设备,要么修改成https协议,要么就file:///url
修改成Https协议存在一个问题,证书的问题,还有就是一般情况下外网配个域名才采用https协议。
该案例中使用localhost || 127.0.0.1 是可以正常访问的,但是使用http://ip方式访问时因为没有证书会报错
这里需要把http请求换成https的方式进行请求
方法一:
// vue.config.jsdevServer: {https: true} // 可以在本地开启 https 服务
方法二:
如何使用谷歌浏览器通过IP访问多媒体设备?
其实很简单,就是让谷歌浏览器开放某个地址的权限
1 打开谷歌浏览器,在地址栏输入:
chrome://flags/#unsafely-treat-insecure-origin-as-secure

https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia
video
poster: 视频封面
muted: 设置或返回视频是否应该被静音(关闭声音)
playsinline: 禁止使用进度条
onloadedmetadata: 事件在指定视频/音频(audio/video)的元数据加载后触发
码云项目
https://gitee.com/TsMask/face-api-demo-vue
npm install face-api.js -S
api:
https://justadudewhohacks.github.io/face-api.js/docs/globals.html
vue文件
训练模型文件,放置public/models 下
https://github.com/justadudewhohacks/face-api.js/tree/master/weights
实现代码
先加载模型
async initFaceApiModel() {try {// 加载所有模型数据,models 是存放模型数据文件的目录// await faceapi.loadModels('/models');await faceapi.nets[this.nets].loadFromUri("/models"); // 算法模型await faceapi.loadFaceLandmarkModel("/models"); // 轮廓模型await faceapi.loadFaceExpressionModel("/models"); // 表情模型await faceapi.loadAgeGenderModel("/models"); // 年龄模型// 根据算法模型参数识别调整结果switch (this.nets) {case "ssdMobilenetv1":this.options = new faceapi.SsdMobilenetv1Options({// 最小置信值,大于等于这个值才被认为检测到了人脸,然后返回一个detection对象minConfidence: 0.5, // 0.1 ~ 0.9// 最多返回人脸的个数maxResults: 100 // default: 100});break;case "tinyFaceDetector":// 模型的配置参数// 实测下来,Tiny Face Detector 模型的性能非常好,检测的准确度也不错,只有人脸很小的时候,会有较大偏差,scoreThreshold 阈值为 0.6 时最佳this.options = new faceapi.TinyFaceDetectorOptions({// 输入的数据源大小,这个值越小,处理速度越快。常用值:128, 160, 224, 320, 416, 512, 608inputSize: 512, // default: 416// // 用于过滤边界的分数阈值,大于等于这个值才被认为检测到了人脸,然后返回一个detection对象, 0.1 ~ 0.9scoreThreshold: 0.6, // default: 0.5});break;case "mtcnn":// 多任务级联卷积神经网络this.options = new faceapi.MtcnnOptions({// 人脸尺寸的最小值,小于这个尺寸的人脸不会被检测到minFaceSize: 20, // default: 20// 比例因子用于计算图像的比例步长scaleFactor: 0.709, // 0.1 ~ 0.9 default: 0.709});break;}// 节点属性化this.videoEl = document.getElementById("myVideo");this.canvasEl = document.getElementById("myCanvas");} catch (err) {console.log(err)}},
唤起摄像设备
navigator.mediaDevices.getUserMedia
视频流用video标签在页面呈现
<divclass="facePhoto__see"><videoid="myVideo"class="facePhoto__video"@loadedmetadata="fnRun"></video><canvasid="myCanvas"class="facePhoto__canvas"style='margin: 0;padding: 0;'></canvas></div>
loadedmetadata 监听视频元数据已加载事件, 在这里用算法处理视频流
绘制到canvas上
// 人脸面部勘探轮廓识别绘制async fnRunFaceLandmark() {console.log("RunFaceLandmark");if (this.videoEl.paused) return clearTimeout(this.timeout);// 识别绘制人脸信息const result = await faceapi[this.detectFace](this.videoEl,this.options).withFaceLandmarks();console.log('result===', result)if (isArray(result) && result.length > 1) {this.$toast({message: '检测出多张人脸',duration: 800})} else if (isArray(result) && result.length === 1 && !this.videoEl.paused) {const dims = faceapi.matchDimensions(this.canvasEl, this.videoEl, true);const resizeResult = faceapi.resizeResults(result, dims);this.withBoxes? faceapi.draw.drawDetections(this.canvasEl, resizeResult): faceapi.draw.drawFaceLandmarks(this.canvasEl, resizeResult);} else {this.canvasEl.getContext("2d").clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);}this.timeout = setTimeout(() => this.fnRunFaceLandmark(), 1000); // 递归调用,实时检测},
// 获取照片 base64 (扩展:相机拍照功能,保存本地相册)// 头像照片captureAvg() {var vm = this;var ctx = this.canvasEl.getContext('2d'),CHeight = this.videoEl.clientHeight, //获取屏幕大小让canvas自适应CWidth = this.videoEl.clientWidth;this.canvasEl.width = CWidth;this.canvasEl.height = CHeight;if (vm.localMediaStream) {console.log(ctx)ctx.drawImage(this.videoEl, 0, 0, CWidth, CHeight); // 向画布上绘制图片// HTMLCanvasElement.toDataURL() 返回一个包含图片展示的data url (base64)var dataURL = this.canvasEl.toDataURL('image/jpeg'); //dataURL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA'console.log('dataURL---', dataURL)vm.imginfo = dataURL;// 停止摄像机this.videoEl.pause();this.stopCapture();}},
