<article class=“article fmt article-content”><h2>相干介绍</h2><h3>车主认证我的项目背景</h3><p>车主认证主体是以H5模式存在的,目前投放在多端,包含:哈啰App、车主App、货运车主App、支付宝小程序、微信小程序、H5外投页面,存在多端场景调用拍摄能力的需要。</p><p>存在问题:</p><ul><li>多平台适配<br/>确保拍摄性能在各个平台上有良好的适配,包含哈啰App、车主App、货运车主App、支付宝小程序、微信小程序和H5外投页面。</li><li>小程序兼容性<br/>对于支付宝小程序和微信小程序,要确保拍摄性能在小程序环境下可能失常调用。支付宝小程序目前借助小程序自身的拍摄能力,然而微信未提供视频拍摄计划。</li><li>外投页面兼容性<br/>对于H5外投页面,可能会面临不同浏览器和设施的兼容性挑战。确保在各种浏览器中都可能失常加载和运行。</li></ul><h3>WebRTC简介</h3><p>WebRTC (Web Real-Time Communications) 是一项实时通信技术,在不借助两头媒介的状况下,建设浏览器之间点对点(Peer-to-Peer)的连贯,是一组用于在Web浏览器和挪动应用程序中实现实时通信的凋谢规范和协定。它容许浏览器和应用程序之间通过简略的API实现音频、视频和数据的实时传输。</p><p>WebRTC 的典型利用场景包含实时视频通话、视频会议、屏幕共享、音视频录制等。</p><p>WebRTC次要蕴含以下三个外围模块:</p><ul><li>getUserMedia: 用于获取用户的音频和视频流。次要利用在视频和音频录制、视频通话和音频通话、在线会议和近程合作、人脸识别和图像处理等。</li><li>RTCPeerConnection: 用于建设点对点的连贯,反对实时的音频和视频传输。次要利用在实时音视频通话、视频会议、屏幕共享等。</li><li>RTCDataChannel: 用于在两个对等体之间传输任意数据。次要利用在文件传输、实时游戏、即时消息、协同编辑、近程管制等。</li></ul><p>因为其 API 的多样,针对不同的场景,其余贡献者们做了无效封装,recordRTC 就是其中一个。 其基于WebRTC的 getUserMedia API 实现媒体设施拜访, 并对 WebRTC提供的视频流函数进行了封装, 使开发者能够简略函数调用就能实现视频录制。</p><p>本计划的实现借助了WebRTC和RecordRTC的图像采集以及媒体数据流(getUserMedia)的控制能力,WebRTC的外围还包含实时传输、平安传输等等,有趣味的同学能够自行理解。</p><h3>recordRTC简介</h3><p>recordRTC 是一个 JavaScript 库,提供了一些用于录制媒体流(如音频、视频)的性能。 基于 WebRTC 的 getUserMedia API,利用这一API,它能够获取用户的音频和视频流。以下是 recordRTC 利用 getUserMedia 提供的次要能力:</p><ul><li>获取摄像头和麦克风的拜访权限: 通过 getUserMedia,recordRTC 能够申请用户授予对摄像头和麦克风的拜访权限。用户能够抉择容许或回绝这些权限。</li><li>获取媒体流: getUserMedia 返回一个代表用户摄像头和麦克风的媒体流对象。这个媒体流蕴含实时的音频和视频数据。</li><li>媒体流的配置: 通过 getUserMedia 的配置参数,recordRTC 能够指定获取的媒体流的个性,例如抉择前置或后置摄像头、指定视频分辨率、抉择音频输出设施等。</li><li>实时预览: getUserMedia 容许在获取媒体流后进行实时的音视频预览。</li><li>动静更新媒体流: getUserMedia 提供了一些办法,在运行时能够动静更新媒体流的配置,例如切换摄像头、更改分辨率等。</li></ul><p>反对的浏览器:</p><p></p><p>罕用参数:</p><ul><li>type: 承受 video or audio or canvas or gif</li><li>recorderType: 承受 MediaStreamRecorder or StereoAudioRecorder or WhammyRecorder or GifRecorder</li><li>timeSlice: 承受一个毫秒数; 用它来强制基于距离的blob</li><li>ondataavailable: 将此函数与timeSlice一起传递以获取基于距离的blob</li><li>bitsPerSecond: 每秒比特数; 实用于音频和视频的轨道</li><li>audioBitsPerSecond: 每秒比特数; 只实用于音频轨道</li><li>videoBitsPerSecond: 每秒比特数; 只实用于视频轨道</li><li>disableLogs: 承受 true or false; 用它禁用console的日志输入</li><li>frameInterval: 承受一个毫秒数</li><li>previewStream: 是 MultiStreamRecorder 的回调办法</li><li>video: 承受一个相似对象: {width: 320, height: 240}</li><li>canvas: 承受一个相似对象: {width: 320, height: 240}</li></ul><p>办法:</p><ul><li>startRecording(): 启动录制过程。调用此办法将开始捕捉媒体流,并开始录制音频或视频。</li><li>stopRecording(callback): 进行录制过程。能够传递一个回调函数,用于在录制实现后处理录制的数据。</li><li>getBlob(): 获取录制数据的 Blob 对象。能够通过此办法获取录制的音频或视频数据。</li><li>pauseRecording(): 暂停录制。能够在录制过程中调用此办法以暂停录制。</li><li>resumeRecording(): 复原录制。在暂停录制后,能够调用此办法以复原录制过程。</li><li>clearRecordedData(): 革除录制的数据。</li><li>getDataURL(callback): 获取录制数据的 Data URL。通过回调函数获取录制的音频或视频数据的 Data URL。</li><li>setRecordingDuration(milliseconds): 设置录制的时长。能够通过此办法设置录制的最大时长,录制达到指定时长后会主动进行。</li></ul><h2>WebRTC拍摄具体实现</h2><h3>拍摄流程</h3><p></p><h3>具体实现</h3><h4>装置:</h4><p>装置 recordrtc 库,引入 RecordRTCPromisesHandler 类,用于解决WebRTC的视频录制。</p><pre><code>npm install recordrtcimport { RecordRTCPromisesHandler } from ‘recordrtc’;</code></pre><h4>应用:</h4><p>在车主认证我的项目中,将操作js拍摄化封装为一个 video-recorder 组件,在组件外部解决办法调用。</p><p>具体实现步骤大略分为3局部:</p><ul><li>初始化:获取拍摄设施和配置信息;</li><li>拍摄:应用 RecordRTCPromisesHandler 的实例化对象提供的办法;</li><li>上传:视频上传到阿里云OSS,并且进行回显。</li></ul><h5>初始化:</h5><p></p><p>因为目前手机存在多个后置摄像头场景,如果获取到的是广角或者桌面视角摄像头,则会有体验问题,所以在初始化时,将所有后置摄像头全副获取,能够让用户通过 Picker 进行抉择。</p><p></p><p></p><p>getVideoConstraints办法,获取后置拍摄设施配置列表。</p><pre><code>async getVideoConstraints() { let deviceId = ‘’; // 只有第一次时须要遍历镜头列表 if (!this.activeCamera) { // 获取所有设施列表 const deviceList = await navigator.mediaDevices.enumerateDevices(); // 过滤出视频输出设施列表 const videoDeviceList = deviceList.filter((deviceInfo) => deviceInfo.kind === ‘videoinput’).reverse(); // 发送视频设施列表到父组件 this.$emit(‘output-list’, videoDeviceList); // 遍历视频输出设施列表 for (const device of videoDeviceList) { // 获取特定设施的视频流 const stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: device.deviceId, }, audio: false, }); // 查看摄像头是否为环境(后置)摄像头 const isEnvironment = stream.getVideoTracks()[0].getSettings().facingMode === ’environment’; // 进行获取的视频流上的所有轨道,开释资源 stream.getTracks().forEach((track) => { track.stop(); }); // 如果是环境(后置)摄像头,则记录设施ID,并跳出循环 if (isEnvironment) { deviceId = device.deviceId; break; } } } // 设置视频束缚 const result: MediaTrackConstraints = { frameRate: { ideal: 6, max: 10 }, width: this.env.isAndroid ? { ideal: 960, min: 480, max: 960 } : { ideal: 480, min: 480, max: 960 }, height: this.env.isAndroid ? { ideal: 1280, min: 640, max: 1280 } : { ideal: 640, min: 640, max: 1280 }, facingMode: ’environment’, deviceId: this.activeCamera ? this.activeCamera.deviceId : deviceId, aspectRatio: 3 / 4, }; if (!deviceId && !this.activeCamera) { delete result.deviceId; } // 返回视频束缚 return result;}</code></pre><h5>拍摄:</h5><p>点击录制按钮,通过调用 recorder 对象的 startRecording 办法来开启视频录制。</p><pre><code>async record() { if (this.recorder) { await this.recorder.startRecording(); this.isRecording = true; }}</code></pre><p>在开启录制后,倒计时5s,进行录制,调用 recorder 对象的 stopRecording 进行拍摄,通过 getBlob() 办法获取录制的 Blob对象,肯定要在进行录制之后获取 Blob 对象,否则可能获取的Blob数据有问题。</p><pre><code>// 开始倒计时 startTimer() { if (this.timerText > 1) { this.recording = true; this.timerText -= 1; setTimeout(() => { this.startTimer(); }, 1000); } else { this.resetTimer(); } }// 倒计时完结后重制 resetTimer() { if (this.$refs.videoRecorder) { this.$refs.videoRecorder.stop(); } this.recording = false; this.btnImgUrl = btnImgUrlMapper.DEFALUT; this.timerText = 6; }// 进行拍摄并且上传文件 async stop() { if (this.recorder) { await this.recorder.stopRecording(); this.isRecording = false; this.uploadFile(); } }// 获取视频流 async uploadFile() { const video = await this.recorder.getBlob(); this.$emit(‘recorded’, { video, }); }</code></pre><h5>上传:</h5><p>视频上传是应用 aliyun 的 oss,在获取到 上传视频的 Blob 对象之后,上传到 aliyun 进行存储,通过返回的文件名 videoRes.name 获取视频的预览Url,跳转到Ocr辨认页,进行Ocr辨认。</p><p></p><p>(本文作者:佟健)</p><p></p></article>