今天我們就來實(shí)現(xiàn)下采集的部分,來快速入下門,直觀感受下 WebRTC 能做什么吧。我們會(huì)實(shí)現(xiàn)屏幕的錄制、攝像頭的錄制,并且能夠回放錄制的內(nèi)容,還支持下載。
[[441095]]
不知你是否用過 web 版的視頻面試,或者 web 版在線會(huì)議,它們都支持分享屏幕、也能開啟攝像頭。這些都是瀏覽器上實(shí)現(xiàn)的,作為前端開發(fā),是否好奇過這些功能的實(shí)現(xiàn)原理呢?
瀏覽器上的音視頻通信相關(guān)的能力叫做 WebRTC(real time communication),是隨著網(wǎng)速越來越快、音視頻需求越來越多,而被瀏覽器所實(shí)現(xiàn)的音視頻的標(biāo)準(zhǔn) API。
音視頻通信的流程有五步:采集、編碼、通信、解碼、渲染。
這五步比較好理解,但是每一步都有挺多內(nèi)容的。
今天我們就來實(shí)現(xiàn)下采集的部分,來快速入下門,直觀感受下 WebRTC 能做什么吧。
我們會(huì)實(shí)現(xiàn)屏幕的錄制、攝像頭的錄制,并且能夠回放錄制的內(nèi)容,還支持下載。
那我們開始吧。
思路分析
瀏覽器提供了 navigator.mediaDevices.getDisplayMedia 和 navigator.mediaDevices.getUserMedia 的 api,分別可以用來獲取屏幕的流、麥克風(fēng)和攝像頭的流。
從名字就可以看出來 getDisplayMedia 獲取的是屏幕的流,getUserMedia 獲取的是和用戶相關(guān)的,也就是麥克風(fēng)、攝像頭這些的流。
獲取流之后設(shè)置到 video 的 srcObject 屬性上就可以實(shí)現(xiàn)播放。
如果想要錄制視頻,需要用 MediaRecorder 的 api,它可以監(jiān)聽流中的數(shù)據(jù),我們可以把獲取到的數(shù)據(jù)保存到數(shù)組中。然后回放的時(shí)候設(shè)置到另一個(gè)視頻的 srcObject 屬性就可以了。
下載也是基于 MediaRecorder 錄制的數(shù)據(jù),轉(zhuǎn)成 blob 后通過 a 標(biāo)簽觸發(fā)下載。
大概理清了思路,我們來寫下代碼。
代碼實(shí)現(xiàn)
我們在頁面放兩個(gè) video 標(biāo)簽,一個(gè)用于實(shí)時(shí)的看錄制的視頻,一個(gè)用于回放。
然后放幾個(gè)按鈕。
- <selection>
- <video autoplay id = "player"></video>
- <video id = "recordPlayer"></video>
- </selection>
- <section>
- <button id = "startScreen">開啟錄屏</button>
- <button id = "startCamera">開啟攝像頭</button>
- <button id = "stop">結(jié)束</button>
- <button id = "reply">回放</button>
- <button id = "download">下載</button>
- </selection>
“開始錄屏” 和 “開啟攝像頭” 按鈕點(diǎn)擊的時(shí)候都開啟錄制,但是方式不同。
- startScreenBtn.addEventListener('click', () => {
- record('screen');
- });
- startCameraBtn.addEventListener('click', () => {
- record('camera');
- });
一個(gè)是用 getUserMedia 的 api 來獲取麥克風(fēng)、攝像頭數(shù)據(jù),一個(gè)是用 getDisplayMedia 的 api 獲取屏幕數(shù)據(jù)。
- async function record(recordType) {
- const getMediaMethod = recordType === 'screen' ? 'getDisplayMedia' : 'getUserMedia';
- const stream = await navigator.mediaDevices[getMediaMethod]({
- video: {
- width: 500,
- height: 300,
- frameRate: 20
- }
- });
-
- player.srcObject = stream;
- }
指定下寬高和幀率等參數(shù),把返回的流設(shè)置到 video 的 srcObject 屬性上,就可以實(shí)時(shí)看到對(duì)應(yīng)的音視頻。
然后,還要做錄制,需要用 MediaRecorder 的 api,傳入 stream,然后調(diào)用 start 方法,開啟錄制。
- let blobs = [], mediaRecorder;
-
- mediaRecorder = new MediaRecorder(stream, {
- mimeType: 'video/webm'
- });
- mediaRecorder.ondataavailable = (e) => {
- blobs.push(e.data);
- };
- mediaRecorder.start(100);
start 的參數(shù)是分割的大小,傳入 100 代表每 100ms 保存一次數(shù)據(jù)。
監(jiān)聽 dataavailable 事件,在其中把獲取到的數(shù)據(jù)保存到 blobs 數(shù)組中。
之后根據(jù) blobs 數(shù)組生成 blob,就可以分別做回放和下載了:
回放:
- replyBtn.addEventListener('click', () => {
- const blob = new Blob(blobs, {type : 'video/webm'});
- recordPlayer.src = URL.createObjectURL(blob);
- recordPlayer.play();
- });
blob 要經(jīng)過 URL.createObjectURL 的處理,才能作為 object url 來被播放。
下載:
- download.addEventListener('click', () => {
- var blob = new Blob(blobs, {type: 'video/webm'});
- var url = URL.createObjectURL(blob);
-
- var a = document.createElement('a');
- a.href = url;
- a.style.display = 'none';
- a.download = 'record.webm';
- a.click();
- });
生成一個(gè)隱藏的 a 標(biāo)簽,設(shè)置 download 屬性就可以支持下載。然后觸發(fā) click 事件。
目前為止,我們已經(jīng)實(shí)現(xiàn)了麥克風(fēng)、攝像頭、屏幕的錄制,支持了回放和下載。
這里也貼一份:
- <html>
- <head>
- <title>錄屏并下載</title>
- </head>
- <body>
- <selection>
- <video autoplay id = "player"></video>
- <video id = "recordPlayer"></video>
- </selection>
- <section>
- <button id = "startScreen">開啟錄屏</button>
- <button id = "startCamera">開啟攝像頭</button>
- <button id = "stop">結(jié)束</button>
- <button id = "reply">回放</button>
- <button id = "download">下載</button>
- </selection>
-
- <script>
- const player = document.querySelector('#player');
- const recordPlayer = document.querySelector('#recordPlayer');
- let blobs = [], mediaRecorder;
-
- async function record(recordType) {
- const getMediaMethod = recordType === 'screen' ? 'getDisplayMedia' : 'getUserMedia';
- const stream = await navigator.mediaDevices[getMediaMethod]({
- video: {
- width: 500,
- height: 300,
- frameRate: 20
- }
- });
- player.srcObject = stream;
-
- mediaRecorder = new MediaRecorder(stream, {
- mimeType: 'video/webm'
- });
- mediaRecorder.ondataavailable = (e) => {
- blobs.push(e.data);
- };
- mediaRecorder.start(100);
- }
-
- const downloadBtn = document.querySelector('#download');
- const startScreenBtn = document.querySelector('#startScreen');
- const startCameraBtn = document.querySelector('#startCamera');
- const stopBtn = document.querySelector('#stop');
- const replyBtn = document.querySelector('#reply');
-
- startScreenBtn.addEventListener('click', () => {
- record('screen');
- });
- startCameraBtn.addEventListener('click', () => {
- record('camera');
- });
-
- stopBtn.addEventListener('click', () => {
- mediaRecorder && mediaRecorder.stop();
- });
-
- replyBtn.addEventListener('click', () => {
- const blob = new Blob(blobs, {type : 'video/webm'});
- recordPlayer.src = URL.createObjectURL(blob);
- recordPlayer.play();
- });
-
- download.addEventListener('click', () => {
- var blob = new Blob(blobs, {type: 'video/webm'});
- var url = URL.createObjectURL(blob);
-
- var a = document.createElement('a');
- a.href = url;
- a.style.display = 'none';
- a.download = 'record.webm';
- a.click();
- });
- </script>
- </body>
- </html>
總結(jié)
音視頻通信分為 采集、編碼、通信、解碼、渲染 這五步,瀏覽器的音視頻通信相關(guān)的 API 叫做 WebRTC。
我們實(shí)現(xiàn)了下采集的部分來入門了下 WebRTC,還支持了回放和下載。
涉及到的 api 有 3 個(gè):
- navigator.mediaDevices.getUserMedia:獲取麥克風(fēng)、攝像頭的流
- navigator.mediaDevices.getDisplayMedia:獲取屏幕的流
- MediaRecorder:監(jiān)聽流的變化,實(shí)現(xiàn)錄制
我們分別用前兩個(gè) api 獲取到了屏幕、麥克風(fēng)、攝像頭的流,然后用 MediaRecorder 做了錄制,把數(shù)據(jù)保存到數(shù)組中,之后生成了 Blob。
video 可以設(shè)置 srcObject 屬性為一個(gè)流,這樣能直接播放,如果設(shè)置 Blob 的話需要用 URL.createObjectURL 處理下。
下載的實(shí)現(xiàn)是通過 a 標(biāo)簽指向 Blob 對(duì)象的 object url,通過 download 屬性指定下載行為,然后手動(dòng)觸發(fā) click 來下載。
我們學(xué)會(huì)了如何用 WebRTC 來采集數(shù)據(jù),這是音視頻通信的數(shù)據(jù)來源,之后還要實(shí)現(xiàn)編解碼和通信才能是完整 RTC 流程,這些后續(xù)再深入。
我們直觀的感受了下 WebRTC 能做什么,是不是感覺這個(gè)領(lǐng)域也挺有趣的呢?