HTML躬行記(2)——WebRTC基礎實踐

  WebRTC (Web Real-Time Communications) 是一項實時通訊技術,在 2011 年由 Google 提出,經過 10 年的發展,W3C 於 2021 年正式發佈 WebRTC 1.0 標準。

  

  WebRTC 標準概括介紹了兩種不同的技術:媒體捕獲設備和點對點連接(P2P,Peer-to-Peer),可讓用戶無需安裝任何插件或第三方軟件的情況下,實現共享桌面、文件傳輸、視頻直播等功能。

  下圖是官方給出的一張 WebRTC 整體架構設計圖:

  

  • 紫色部分是前端開發所使用的 API。
  • 藍色實線部分是各大瀏覽器廠商所使用的 API。
  • 藍色虛線部分包含可自定義的 3 塊:音頻引擎、視頻引擎和網絡傳輸。

  由於各個瀏覽器對 WebRTC 的實現有所不同,因此 Google 官方提供了一個適配器腳本庫:adapter.js,省去很多兼容工作。

  本文的源碼已上傳至 Github,有需要的可以隨意下載。

一、自拍

  自拍是指通過攝像頭拍照生成圖片,先看下 HTML 結構,其實就 4 個元素。

<video id="video"></video>
<button id="btn">拍照</button>
<canvas id="canvas" width="300" height="300"></canvas>
<img id="img" alt="照片"/>

1)getUserMedia()

  然後在腳本中聲明各個元素,通過 navigator.mediaDevices.getUserMedia() 方法獲取媒體流。

const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const btn = document.getElementById('btn');
const img = document.getElementById('img');
const size = 300;
/**
 * 獲取媒體流
 */
navigator.mediaDevices.getUserMedia({ 
  video: {
    width: size, 
    height: size,
  }, 
  audio: false 
}).then((stream) => {
  video.srcObject = stream;
  video.play();
});

  getUserMedia() 的參數是一個包含了video 和 audio 兩個成員的 MediaStreamConstraints 對象,上面代碼將攝像頭的分辨率限制爲 300 x 300。

  then() 中的 stream 參數是一個 MediaStream 媒體流,一個流相當於容器,可以包含幾條軌道,例如視頻和音頻軌道,每條軌道都是獨立的。

  video 元素中的 src 和 srcObject 是一對互斥的屬性,後者可關聯媒體源,根據規範也可以是 Blob 或者 File 等類型的數據。

  接着爲按鈕綁定點擊事件,並且在點擊時從流中捕獲幀,畫到 Canvas 內,再導出賦給 img 元素。

/**
 * 點擊拍照
 */
btn.addEventListener('click', (e) => {
  const context = canvas.getContext('2d');
  // 從流中捕獲幀
  context.drawImage(video, 0, 0, size, size);
  // 將幀導出爲圖片
  const data = canvas.toDataURL('image/png');
  img.setAttribute('src', data);
}, false);

  在下圖中,左邊是 video 元素,打開攝像頭後就會有畫面,在點擊拍照按鈕後,右邊顯示捕獲的幀。

  

2)enumerateDevices()

  MediaDevices 提供了訪問媒體輸入和輸出的設備,例如攝像頭、麥克風等,得到硬件資源的媒體數據。

  mediaDevices.enumerateDevices() 會得到一個描述設備的 MediaDeviceInfo 的數組。

  其中 groupId 用於標識多個設備屬於同一個物理設備,例如一個顯示器內置了攝像頭和麥克風。

navigator.mediaDevices.enumerateDevices()
.then((devices) => {
  devices.forEach((device) => {
    console.log(`${device.kind}: ${device.label} id = ${device.deviceId}`);
  });
})

3)devicechange

  當媒體設備(例如麥克風、攝像頭等)連接到系統或從系統中移除時,devicechange 事件就會被髮送給設備實例。

navigator.mediaDevices.ondevicechange = (event) => { };

  event 參數沒有附加任何特殊的屬性。

二、共享桌面

  Windows 系統採用的共享桌面協議是 RDP(Remote Desktop Protocal),另一種可在不同操作系統共享桌面的協議是 VNC(Virtual Network Console)。

  像 TeamViewer 採用的就是後一種協議,而 WebRTC 的遠程桌面沒有采用傳統的 RDP、VNC 等協議,因爲不需要遠程控制。

  WebRTC 提供了 getDisplayMedia() 方法採集桌面,在使用上與之前的 getUserMedia() 方法類似。

navigator.mediaDevices.getDisplayMedia({ 
  video: {
    width: 2000,
    height: 1000
  }
}).then((stream) => {
  video.srcObject = stream;
  video.play();
});

  在刷新頁面後,會要求選擇共享的桌面,包括整個屏幕、窗口或 Chrome 標籤頁。

  

三、錄像

  WebRTC 的錄像包括錄製音頻和視頻兩種流,通過 Blob 對象將數據保存成多媒體文件。

1)MediaRecorder

  WebRTC 提供了 MediaRecorder 類,它能接收兩個參數,第一個是遠程的 MediaStream 媒體流,第二個是配置項。

  其配置項包括編解碼器、音視頻碼率、容器的 MIME 類型(例如 video/webm、video/mp4 )等相關信息。

  先看個示例,HTML結構如下所示,一個 video 元素和兩個 button 元素:回放和下載。

<video id="video"></video>
<button id="playback">回放</button>
<button id="download">下載</button>

  然後看下錄像的整體邏輯,和之前自拍一節類似,也需要調用 getUserMedia() 獲取媒體流。

  在 then() 的回調中實例化 MediaRecorder 類,並配置多媒體格式。

  其中WebM是一個由Google資助,免版權費用的視頻文件格式;VP8是一個開放的影像壓縮格式。

const video = document.getElementById('video');
const playback = document.getElementById('playback');
const download = document.getElementById('download');
const size = 300;
const chunks = [];    // 一個由 Blob 對象組成的數組

navigator.mediaDevices.getUserMedia({ 
  video: {
    width: size, 
    height: size,
  }, 
  audio: true 
}).then((stream) => {
  // 配置多媒體格式
  const options = { mimeType: 'video/webm;codecs=vp8' };
  // 實例化錄製對象
  const recorder = new MediaRecorder(stream, options);
  // 當收到數據時觸發該事件
  recorder.ondataavailable = function(e) {
    chunks.push(e.data);    // data 是一個可用的 Blob 對象
  }
  // 開始錄製
  recorder.start(10);
});

  recorder 的 dataavailable 事件會在收到數據時觸發,e 參數的 data 屬性是一個可用的 Blob 對象。

  最後在開始錄製調用 start() 方法時,可以配置一個毫秒級的時間片,那麼在錄製時會按照配置的值分割成一個個單獨的區塊,而不是錄製一個非常大的整塊內容。

  分塊可以提高效率和可靠性,如果是一整塊,那麼會變得越來越大,讀寫效率也會變差。

2)回放

  首先根據 chunks 生成 Blob 對象,再根據 Blob 對象生成 URL 對象。

playback.addEventListener('click', () => {
  // 根據 chunks 生成 Blob 對象
  const blob = new Blob(chunks, {type: 'video/webm'});
  // 根據 Blob 對象生成 URL 對象
  video.src = window.URL.createObjectURL(blob);
  video.play();
}, false);

  URL.createObjectURL 是一個靜態方法,返回值是一個指定的 File 對象或 Blob 對象。

3)下載

  首先與回放一樣,也是生成一個 URL 對象,然後創建 a 元素,將對象賦給 href 屬性。

  並且要指定 download 屬性,告訴瀏覽器下載 URL 而不是導航。

download.addEventListener('click', (e) => {
  const blob = new Blob(chunks, {type: 'video/webm'});
  const url = window.URL.createObjectURL(blob);
  // 創建 a 元素
  const a = document.createElement('a');
  a.href = url;
  // 指示瀏覽器下載 URL 而不是導航
  a.download = 'test.webm';
  a.click();
}, false);

 

參考資料:

WebRTC官方

WebRTC MDN

Build the backend services needed for a WebRTC app

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章