相關介紹
車主認證項目背景
車主認證主體是以H5形式存在的,目前投放在多端,包括:哈囉App、車主App、貨運車主App、支付寶小程序、微信小程序、H5外投頁面,存在多端場景調用拍攝能力的需求。
存在問題:
- 多平台適配
確保拍攝功能在各個平台上有良好的適配,包括哈囉App、車主App、貨運車主App、支付寶小程序、微信小程序和H5外投頁面。 - 小程序兼容性
對於支付寶小程序和微信小程序,要確保拍攝功能在小程序環境下能夠正常調用。支付寶小程序目前藉助小程序本身的拍攝能力,但是微信未提供視頻拍攝方案。 - 外投頁面兼容性
對於H5外投頁面,可能會面臨不同瀏覽器和設備的兼容性挑戰。確保在各種瀏覽器中都能夠正常加載和運行。
WebRTC簡介
WebRTC (Web Real-Time Communications) 是一項實時通訊技術,在不借助中間媒介的情況下,建立瀏覽器之間點對點(Peer-to-Peer)的連接,是一組用於在Web瀏覽器和移動應用程序中實現實時通信的開放標準和協議。它允許瀏覽器和應用程序之間通過簡單的API實現音頻、視頻和數據的實時傳輸。
WebRTC 的典型應用場景包括實時視頻通話、視頻會議、屏幕共享、音視頻錄製等。
WebRTC主要包含以下三個核心模塊:
- getUserMedia: 用於獲取用户的音頻和視頻流。主要應用在視頻和音頻錄製、視頻通話和音頻通話、在線會議和遠程協作、人臉識別和圖像處理等。
- RTCPeerConnection: 用於建立點對點的連接,支持實時的音頻和視頻傳輸。主要應用在實時音視頻通話、視頻會議、屏幕共享等。
- RTCDataChannel: 用於在兩個對等體之間傳輸任意數據。主要應用在文件傳輸、實時遊戲、即時消息、協同編輯、遠程控制等。
由於其 API 的多樣,針對不同的場景,其他貢獻者們做了有效封裝,recordRTC 就是其中一個。 其基於WebRTC的 getUserMedia API 實現媒體設備訪問, 並對 WebRTC提供的視頻流函數進行了封裝, 使開發者可以簡單函數調用就能實現視頻錄製。
本方案的實現藉助了WebRTC和RecordRTC的圖像採集以及媒體數據流(getUserMedia)的控制能力,WebRTC的核心還包括實時傳輸、安全傳輸等等,有興趣的同學可以自行了解。
recordRTC簡介
recordRTC 是一個 JavaScript 庫,提供了一些用於錄製媒體流(如音頻、視頻)的功能。 基於 WebRTC 的 getUserMedia API,利用這一API,它可以獲取用户的音頻和視頻流。以下是 recordRTC 利用 getUserMedia 提供的主要能力:
- 獲取攝像頭和麥克風的訪問權限: 通過 getUserMedia,recordRTC 可以請求用户授予對攝像頭和麥克風的訪問權限。用户可以選擇允許或拒絕這些權限。
- 獲取媒體流: getUserMedia 返回一個代表用户攝像頭和麥克風的媒體流對象。這個媒體流包含實時的音頻和視頻數據。
- 媒體流的配置: 通過 getUserMedia 的配置參數,recordRTC 可以指定獲取的媒體流的特性,例如選擇前置或後置攝像頭、指定視頻分辨率、選擇音頻輸入設備等。
- 實時預覽: getUserMedia 允許在獲取媒體流後進行實時的音視頻預覽。
- 動態更新媒體流: getUserMedia 提供了一些方法,在運行時可以動態更新媒體流的配置,例如切換攝像頭、更改分辨率等。
支持的瀏覽器:
常用參數:
- type: 接受 video or audio or canvas or gif
- recorderType: 接受 MediaStreamRecorder or StereoAudioRecorder or WhammyRecorder or GifRecorder
- timeSlice: 接受一個毫秒數; 用它來強制基於間隔的blob
- ondataavailable: 將此函數與timeSlice一起傳遞以獲取基於間隔的blob
- bitsPerSecond: 每秒比特數; 適用於音頻和視頻的軌道
- audioBitsPerSecond: 每秒比特數; 只適用於音頻軌道
- videoBitsPerSecond: 每秒比特數; 只適用於視頻軌道
- disableLogs: 接受 true or false; 用它禁用console的日誌輸出
- frameInterval: 接受一個毫秒數
- previewStream: 是 MultiStreamRecorder 的回調方法
- video: 接受一個類似對象: {width: 320, height: 240}
- canvas: 接受一個類似對象: {width: 320, height: 240}
方法:
- startRecording(): 啓動錄製過程。調用此方法將開始捕獲媒體流,並開始錄製音頻或視頻。
- stopRecording(callback): 停止錄製過程。可以傳遞一個回調函數,用於在錄製完成後處理錄製的數據。
- getBlob(): 獲取錄製數據的 Blob 對象。可以通過此方法獲取錄製的音頻或視頻數據。
- pauseRecording(): 暫停錄製。可以在錄製過程中調用此方法以暫停錄製。
- resumeRecording(): 恢復錄製。在暫停錄製後,可以調用此方法以恢復錄製過程。
- clearRecordedData(): 清除錄製的數據。
- getDataURL(callback): 獲取錄製數據的 Data URL。通過回調函數獲取錄製的音頻或視頻數據的 Data URL。
- setRecordingDuration(milliseconds): 設置錄製的時長。可以通過此方法設置錄製的最大時長,錄製達到指定時長後會自動停止。
WebRTC拍攝具體實現
拍攝流程
具體實現
安裝:
安裝 recordrtc 庫,引入 RecordRTCPromisesHandler 類,用於處理WebRTC的視頻錄製。
npm install recordrtc
import { RecordRTCPromisesHandler } from 'recordrtc';
使用:
在車主認證項目中,將操作js拍攝化封裝為一個 video-recorder 組件,在組件內部處理方法調用。
具體實現步驟大概分為3部分:
- 初始化:獲取拍攝設備和配置信息;
- 拍攝:使用 RecordRTCPromisesHandler 的實例化對象提供的方法;
- 上傳:視頻上傳到阿里雲OSS,並且進行回顯。
初始化:
因為目前手機存在多個後置攝像頭場景,如果獲取到的是廣角或者桌面視角攝像頭,則會有體驗問題,所以在初始化時,將所有後置攝像頭全部獲取,可以讓用户通過 Picker 進行選擇。
getVideoConstraints方法,獲取後置拍攝設備配置列表。
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;
}
拍攝:
點擊錄製按鈕,通過調用 recorder 對象的 startRecording 方法來開啓視頻錄製。
async record() {
if (this.recorder) {
await this.recorder.startRecording();
this.isRecording = true;
}
}
在開啓錄製後,倒計時5s,停止錄製,調用 recorder 對象的 stopRecording 停止拍攝,通過 getBlob() 方法獲取錄製的 Blob對象,一定要在停止錄製之後獲取 Blob 對象,否則可能獲取的Blob數據有問題。
// 開始倒計時
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,
});
}
上傳:
視頻上傳是使用 aliyun 的 oss,在獲取到 上傳視頻的 Blob 對象之後,上傳到 aliyun 進行存儲,通過返回的文件名 videoRes.name 獲取視頻的預覽Url,跳轉到Ocr識別頁,進行Ocr識別。
(本文作者:佟健)