翼度科技»论坛 编程开发 JavaScript 查看内容

Electron调用外接摄像头并拍照上传实现详解

11

主题

11

帖子

33

积分

新手上路

Rank: 1

积分
33
背景

基于
  1. Electron
复制代码
实现的pc端智能验机应用,近期迭代了一个新的功能,需求是通过电脑外接摄像头对手机屏幕进行
  1. 拍照
复制代码
,拍照后需将照片
  1. 上传
复制代码
至服务端进行屏幕信息比对,确定被检测屏幕是否为原厂屏。

需求分析

根据上面的需求,分析大概要以下几个步骤。

  • 先实现将摄像头的画面实时展示在页面视频采集区域中;
  • 将摄像头中的视频画面采集一帧成图片并回显;
  • 将生成的图片上传至CDN拿到图片链接;
  • 将图片链接上传到后端接口 做处理;
确定了需要以上四个步骤后,接下来一步一步实现。

实现


视频采集

由于
  1. Electron
复制代码
内置了
  1. Chromium
复制代码
浏览器,该浏览器对各项前端标准都支持得非常好,所以基于 Electron 开发应用不会遇到浏览器兼容性问题。几乎可以在 Electron 中使用所有
  1. HTML5
复制代码
  1. CSS3
复制代码
  1. ES6
复制代码
标准中定义的
  1. API
复制代码

所以基于
  1. WebRTC
复制代码
提供的
  1. API
复制代码
即可获取到摄像头的视频流。

MediaDevices.getUserMedia()

代码如下:
  1. methods: {
  2.     getUserMedia() {
  3.         /* 可同时开启video(摄像头)和audio(麦克风) 这里只请求摄像头,所以只设置video为true */
  4.         navigator.mediaDevices.getUserMedia({ video: true })
  5.             .then(function(stream) {
  6.               /* 使用这个 stream 传递到成功回调中 */
  7.               this.success(stream)
  8.             })
  9.             .catch(function(err) {
  10.               /* 处理 error 信息 */
  11.               this.error(error)
  12.             });
  13.     }
  14. }
复制代码
  1. MediaDevices.getUserMedia()
复制代码
会提示用户给予使用媒体输入的许可,媒体输入会产生一个
  1. MediaStream
复制代码
,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D 转换器等等),也可能是其它轨道类型。
它返回一个
  1. Promise
复制代码
对象,成功后会
  1. resolve
复制代码
回调一个
  1. MediaStream
复制代码
对象。若找不到满足请求参数的媒体类型,
  1. promise
复制代码
  1. reject
复制代码
回调一个
  1. NotFoundError
复制代码

现在已经成功获取到视频流,接下来就是将视频流回显到页面。 这里使用video标签完成,代码如下:
  1. <template>
  2.     <div class="video-page">
  3.         <div class="video-content">
  4.             <video ref="video" class="video-item"></video>
  5.         </div>
  6.     </div>
  7. </template>
  8. export default {
  9.    methods: {
  10.        getUserMedia() {
  11.             /* 可同时开启video(摄像头)和audio(麦克风) 这里只请求摄像头,所以只设置video为true */
  12.             navigator.mediaDevices.getUserMedia({ video: true })
  13.                 .then(function(stream) {
  14.                   /* 使用这个 stream 传递到成功回调中 */
  15.                   this.success(stream)
  16.                 })
  17.                 .catch(function(err) {
  18.                   /* 处理 error 信息 */
  19.                   this.error(error)
  20.                 });
  21.         },
  22.        success(stream) {
  23.            console.log('成功', stream);
  24.            /* 将stream 分配给video标签 */
  25.            this.$refs.video.srcObject = stream;
  26.            this.$refs.video.play();
  27.         }
  28.     }
  29. }
复制代码
这时,摄像头中的画面就可以显示在页面video标签内,如下图。

为了用户体验,在进入页面之前添加了判断摄像头是否已经接入并可用的逻辑,避免用户的摄像头未接入或者启动,造成应用不可用的错觉。
使用
  1. MediaDevices.enumerateDevices()
复制代码
来获取可用媒体输入和输出设备的列表,例如摄像头、麦克风、耳机等。
  1. navigator.mediaDevices.enumerateDevices().then(devicesList => {
  2.     console.log('------devicesList', deviceList)
  3. })
复制代码
得到的设备列表数据格式如下:
  1. kind
复制代码
类型有三种,分别是
  1. audioinput
复制代码
  1. audiooutput
复制代码
  1. videoinput
复制代码
。分别代表音视频的输入和输出。可在列表中查找目标媒体是否已经接入且可用。
若有选择切换设备需求,可根据
  1. kind
复制代码
类型进行媒体设备分类,选择目标
  1. deviceId
复制代码
,传入
  1. navigator.mediaDevices.getUserMedia
复制代码
,完成来源切换。
  1. navigator.mediaDevices.getUserMedia({ video: { deviceId: xxxx } })
复制代码
拍照生成图片

拍照其实就是截取视频中的某一帧,这里使用
  1. canvas
复制代码
来实现截取。
  1. getContext()
复制代码
方法可返回一个对象,该对象提供了用于在画布上绘图的方法和属性。其中
  1. drawImage()
复制代码
方法用来向画布上绘制图像、画布或视频。
  1. <template>
  2.     <div class="video-page">
  3.         <div class="video-content">
  4.             <video ref="video" class="video-item" v-if="showVideo"></video>
  5.             <canvas ref="canvas" v-else width="500" height="346"></canvas>
  6.         <div class="video-buttons">
  7.             <div @click="capture" class="button-item capture">拍照</div>
  8.             <div @click="submit" class="button-item submit"}">提交</div>
  9.         </div>
  10.     </div>
  11. </template>
  12. export default {
  13.    data: {
  14.        showVideo: true, // 是否展示摄像头画面
  15.    },
  16.    methods: {
  17.         /* 拍照按钮点击 */
  18.         capture() {
  19.           this.showVideo = false
  20.           var context = this.$refs.canvas.getContext('2d');
  21.           /* 要跟video的宽高一致 */
  22.           context.drawImage(this.$refs.video, 0, 0, 1000, 692, 0, 0, 500, 346);
  23.         }
  24.     }
  25. }
复制代码
拍照的图片回显至canvas标签。


上传图片至CDN

上个步骤已经完成了拍照,接下来就需要将图片上传至CDN,拿到图片链接。 这里有两种方式可以实现获取图片数据。

1. 使用HTMLCanvasElement.toBlob()
  1. HTMLCanvasElement.toBlob()
复制代码
方法生成
  1. Blob
复制代码
对象,用以展示 canvas 上的图片。因为直接可以拿到图片文件,所以无需再使用方法2中的函数来转化
  1. base64
复制代码
,直接可以获取到图片文件用来上传。

语法
  1. toBlob(callback, type, quality)
复制代码
参数
  1. callback
复制代码
:回调函数,参数为
  1. Blob
复制代码
对象(目标图片文件)。
  1. type
复制代码
:图片格式,默认为
  1. image/png
复制代码
  1. 可选
复制代码
  1. quality
复制代码
:0-1的数字,表示图片质量,
  1. 可选
复制代码

点击提交按钮按钮时,先获取图片文件,为上传做准备。
  1. methods: {
  2.     /* 提交按钮点击 */
  3.     submit() {
  4.         const base64Url = this.$refs.canvas.toBlob(blob => {
  5.             console.log('===blob', blob)
  6.             const data = new FormData()
  7.             data.append('file', blob)
  8.             request.post('https://XXXXX/upload', data)
  9.         }, "image/jpeg", 0.95)
  10.     }
  11. }
复制代码
console的结果如下图:


2. 使用HTMLCanvasElement.toDataURL()

HTMLCanvasElement.toDataURL()方法返回一个包含图片展示的Data URL。
  1. Data URL
复制代码
,即前缀为 data: 协议的 URL,其允许内容创建者向文档中嵌入小文件。

语法
  1. canvas.toDataURL(type, encoderOptions);
复制代码
参数
  1. type
复制代码
图片格式,默认为
  1. image/png
复制代码
  1. encoderOptions
复制代码
0到1之间的值,用来选定图片质量,默认值是0.92,超出范围会使用默认值。

返回值
  1. base64
复制代码
组成的图片源数据,上传前需转为图片文件。这里封装了一个
  1. convertBase64UrlToImgFile
复制代码
函数用来转换。代码如下:
  1. <template>
  2.     <div class="video-page">
  3.         <div class="video-content">
  4.             <video ref="video" class="video-item" v-if="showVideo"></video>
  5.             <canvas ref="canvas" v-else width="500" height="346"></canvas>
  6.         <div class="video-buttons">
  7.             <div @click="capture" class="button-item capture">拍照</div>
  8.             <div @click="submit" class="button-item submit">提交</div>
  9.         </div>
  10.     </div>
  11. </template>
  12. export default {
  13.    data: {
  14.        /* 是否展示摄像头画面 */
  15.        showVideo: true,
  16.    },
  17.    methods: {
  18.         /* 将base64转为图片文件 */
  19.         convertBase64UrlToImgFile(urlData, fileType) {
  20.             const imgData = urlData.split('base64,').splice(-1)[0]
  21.             /* 解码使用 base-64 编码的字符串 转换为byte */
  22.             const bytes = window.atob(imgData)
  23.             /* 处理异常,将ASCII码小于0的转换为大于0 */
  24.             const ab = new ArrayBuffer(bytes.length)
  25.             const ia = new Int8Array(ab)
  26.             for (let i = 0; i < bytes.length; i++) {
  27.                 ia[i] = bytes.charCodeAt(i)
  28.             }
  29.             /* 转换成文件,可以添加文件的type,lastModifiedDate属性 */
  30.             const blob = new Blob([ab], { type: fileType })
  31.             blob.lastModifiedDate = new Date()
  32.             return blob
  33.         },
  34.         /* 提交按钮点击 */
  35.         async submit() {
  36.             const base64Url = this.$refs.canvas.toDataURL()
  37.             const imgFile = this.convertBase64UrlToImgFile(base64Url, 'image/jpg')
  38.             console.log('====imgFile', imgFile)
  39.             const data = new FormData()
  40.             data.append('file', imgFile)
  41.             /* 上传 */
  42.             request.post('https://XXXXX/upload', data)
  43.         },
  44.     }
  45. }
复制代码
  1. convertBase64UrlToImgFile
复制代码
可用于在使用
  1. canvas
复制代码
外的场景进行
  1. base64
复制代码
转换图片文件。和
  1. HTMLCanvasElement.toBlob()
复制代码
方法得到的结果一致。
以上两种方法都可以完成图片上传,最终拿到CDN图片链接后可传给后端进行处理。获取屏幕信息。

总结

通过以上四个步骤就完成了Electron应用中通过外接摄像头拍照并上传的功能。这里基本用不到Electron的能力,和在web端的实现方式并无区别,Electron在这里起到的作用就是获取摄像头媒体流不需要获取用户权限。
  1. Electron
复制代码
是基于
  1. Chromium
复制代码
  1. Node.js
复制代码
实现的,这就使前端开发者可以使用
  1. JavaScript
复制代码
  1. HTML
复制代码
  1. CSS
复制代码
轻松构建跨平台的桌面应用。
  1. Electron
复制代码
可以使用几乎所有的Web前端生态领域及
  1. Node.js
复制代码
生态领域的组件和技术方案。
后续会介绍Electron在智能验机应用中的实践方案,更多关于Electron调用摄像头拍照上传的资料请关注脚本之家其它相关文章!

来源:https://www.jb51.net/article/276369.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具