博客 / 詳情

返回

WebRTC拍攝在車主認證中的實現

相關介紹

車主認證項目背景

車主認證主體是以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識別。

圖片

(本文作者:佟健)

圖片

user avatar coderleo 頭像 susouth 頭像 ailim 頭像 yilezhiming 頭像 frontoldman 頭像 huanjinliu 頭像 dashnowords 頭像 user_ze46ouik 頭像 cipchk 頭像 wupengyu_55d86cdb45293 頭像 shengmingbuxi_5c1152527848f 頭像 wjchumble 頭像
35 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.