WebRTC (Web Real-Time Communications) 是一項實時通訊技術,在 2011 年由 Google 提出,經過 10 年的發展,W3C 于 2021 年正式發布 WebRTC 1.0 標準。
:-: 
  WebRTC 標準概括介紹了兩種不同的技術:媒體捕獲設備和點對點連接(P2P,Peer-to-Peer),可讓用戶無需安裝任何插件或第三方軟件的情況下,實現共享桌面、文件傳輸、視頻直播等功能。
  下圖是[官方](https://webrtc.github.io/webrtc-org/architecture/)給出的一張 WebRTC 整體架構設計圖:
:-: 
* 紫色部分是前端開發所使用的 API。
* 藍色實線部分是各大瀏覽器廠商所使用的 API。
* 藍色虛線部分包含可自定義的 3 塊:音頻引擎、視頻引擎和網絡傳輸。
  由于各個瀏覽器對 WebRTC 的實現有所不同,因此 Google 官方提供了一個適配器腳本庫:[adapter.js](https://github.com/webrtc/adapter/),省去很多兼容工作。
  本文的源碼已上傳至[Github](https://github.com/pwstrick/webrtc),有需要的可以隨意下載。
## 一、自拍
  自拍是指通過攝像頭拍照生成圖片,先看下 HTML 結構,其實就 4 個元素。
~~~html
<video id="video"></video>
<button id="btn">拍照</button>
<canvas id="canvas" width="300" height="300"></canvas>
<img id="img" alt="照片"/>
~~~
**1)getUserMedia()**
  然后在腳本中聲明各個元素,通過 navigator.[mediaDevices](https://developer.mozilla.org/zh-CN/docs/Web/API/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](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaStream)媒體流,一個流相當于容器,可以包含幾條軌道,例如視頻和音頻軌道,每條軌道都是獨立的。
  video 元素中的 src 和[srcObject](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLMediaElement/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()](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/enumerateDevices)會得到一個描述設備的[MediaDeviceInfo](https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo)的數組。
  其中 groupId 用于標識多個設備屬于同一個物理設備,例如一個顯示器內置了攝像頭和麥克風。
~~~
navigator.mediaDevices.enumerateDevices()
.then((devices) => {
devices.forEach((device) => {
console.log(`${device.kind}: ${device.label} id = ${device.deviceId}`);
});
})
~~~
**3)devicechange**
  當媒體設備(例如麥克風、攝像頭等)連接到系統或從系統中移除時,[devicechange](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/devicechange_event)事件就會被發送給設備實例。
~~~
navigator.mediaDevices.ondevicechange = (event) => { };
~~~
  event 參數沒有附加任何特殊的屬性。
## 二、共享桌面
  Windows 系統采用的共享桌面協議是 RDP(Remote Desktop Protocal),另一種可在不同操作系統共享桌面的協議是 VNC(Virtual Network Console)。
  像 TeamViewer 采用的就是后一種協議,而 WebRTC 的遠程桌面沒有采用傳統的 RDP、VNC 等協議,因為不需要遠程控制。
  WebRTC 提供了[getDisplayMedia()](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getDisplayMedia)方法采集桌面,在使用上與之前的 getUserMedia() 方法類似。
~~~
navigator.mediaDevices.getDisplayMedia({
video: {
width: 2000,
height: 1000
}
}).then((stream) => {
video.srcObject = stream;
video.play();
});
~~~
  在刷新頁面后,會要求選擇共享的桌面,包括整個屏幕、窗口或 Chrome 標簽頁。
:-: 
## 三、錄像
  WebRTC 的錄像包括錄制音頻和視頻兩種流,通過[Blob](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob)對象將數據保存成多媒體文件。
**1)MediaRecorder**
  WebRTC 提供了[MediaRecorder](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder)類,它能接收兩個參數,第一個是遠程的 MediaStream 媒體流,第二個是配置項。
  其配置項包括編解碼器、音視頻碼率、容器的[MIME](https://www.iana.org/assignments/media-types/media-types.xhtml)類型(例如 video/webm、video/mp4 )等相關信息。
  先看個示例,HTML結構如下所示,一個 video 元素和兩個 button 元素:回放和下載。
~~~html
<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](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder/dataavailable_event)事件會在收到數據時觸發,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](https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL)是一個靜態方法,返回值是一個指定的[File](https://developer.mozilla.org/zh-CN/docs/Web/API/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官方](https://webrtc.org/getting-started/overview)
[WebRTC MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API)
[Build the backend services needed for a WebRTC app](https://web.dev/webrtc-infrastructure/)
*****
> 原文出處:
[博客園-HTML躬行記](https://www.cnblogs.com/strick/category/1770829.html)
[知乎專欄-HTML躬行記](https://zhuanlan.zhihu.com/c_1250826149041238016)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎閱讀。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020