基於 WebRTC 與 Webcam 開發一個聊天應用

聲網Agora發表於2019-03-13

如果你在 WebRTC 應用開發過程中遇到問題,歡迎在 RTC 開發者社群與更多有經驗的同行們交流。

WebRTC是一個協議,允許人們使用JavaScript在兩點之間建立實時通訊。

我們可以用這個結構使兩個或更多瀏覽器之間實現直接交流,而不需要中心伺服器。

伺服器只需要在連線的時候被使用,因此每個客戶端知道如何連線彼此。

我們可以使用這個特性建立什麼型別的App呢?例如,直接網路攝像頭連線。點對點通話,檔案共享,還有更多。

本教程我會介紹一個當你第一次使用的時候,會發出驚呼的App:一個網路攝像頭通訊App.

我們不會使用原始WebRTC API,然而,我們需要注意很多細節。這就是library的作用,它們做出了很好的抽象,人們可以集中精力建立App而不是將精力花費的底層API上。

其中一個library是PeerJS,它使得實時通訊變得非常簡單。簡單來說就是WebRTC,它的抽象使得結果獲取更快,之後你可以學習內部是如何運作的。

建議:當你建立一個App時候使用Bit分享到一個可重複利用的集合中,並且在你所有的專案中同步它們!不妨試一下,可以加快你的工作速度。

後端

首先我們需要建立後端。儘管我們會實現直接點對點通訊,起初的握手和合作需要中心伺服器。

基於 WebRTC 與 Webcam 開發一個聊天應用

一旦握手完成,點點之間會直接交流,而不再需要依靠後端。

基於 WebRTC 與 Webcam 開發一個聊天應用

PeerJS為我們提供了這樣一個伺服器,安裝過程很簡單,容易執行。

在一個資料夾下,初始化一個npm專案使用npm init命令,使用npm install peer指令安裝PeerJS,接著你可以使用npx執行它:

Npx peerjs –port 9000
複製程式碼

使用npx peerjs –help檢視更多選擇。

這就是你的後端。

現在我們可以建立一個最簡單的App,我們設定一個接收端和一個傳送端。

前端

首先建立一個接收端,連線PeerJS伺服器,等待接收資訊。第一個引數new Peer()為我們的端點名稱,我們叫做receiver,使得表意更清晰。: 匯入PeerJS客戶端:

<script src="https://cdnjs.cloudflare.com/ajax/libs/peerjs/0.3.16/peer.min.js"></script>
複製程式碼

接著初始化Peer物件。當另一個端點連線我們的時候,Connection事件被呼叫。當接收到一些資訊之後,data事件被呼叫:

const peer = new Peer('receiver', { host: 'localhost', port: 9000, path: '/' })
peer.on('connection', (conn) => {
  conn.on('data', (data) => {
    console.log(data);
  })
})
複製程式碼

讓我們建立通訊的另一端。我們稱作sender,因為它會連線併傳送資訊到接收端。

初始化Peer物件,接著請求端點連線receiver端,接收端我們之前已經建立好。一旦連線建立,open事件啟動,接著呼叫send()方法來向接收端傳送資訊:

const peer = new Peer('sender', { host: 'localhost', port: 9000, path: '/' })
const conn = peer.connect('receiver')
conn.on('open', () => {
  conn.send('hi!')
})
複製程式碼

這就是最簡單的例子。

首先開啟接收端,接著開啟傳送端。接受者從傳送者直接獲取資訊,不需要中心伺服器。伺服器只需要交換資訊,確保兩端連線。之後,就不會干預兩端之間的交流。

這是一個非常基礎的資訊連線。

下一步,我們不會傳送資訊,而是讓兩端彼此共享網路攝像頭流。

在客戶端,我們沒有使用peer.connect()連線,而是使用了peer.call():

const call = peer.call('receiver', localStream)
})
複製程式碼

在接收端,當接收到一個通話事件反饋之後,必須對此作出應答:

peer.on('call', call => {
  call.answer(localStream)
})
複製程式碼

就像電話交流一樣,我們不能自動回覆每個來電,必須明確的作出應答。

每個通話中的LocalStream是什麼?它是網路攝像頭產生的流,我們必須通過呼叫navigator.mediaDevices.getUserMedia()得到它,這是一個瀏覽器API。

這是一個非同步通訊,因此我們使用async/await來等待執行,我們需要將通話包裹在一個非同步函式中,首先:

const startChat = async () => {
  const localStream = await navigator.mediaDevices.getUserMedia({
    video: true
  })
}
startChat()
複製程式碼

一旦我們得到了localStream物件,可以將它分配給HTML網頁中的一個video元素。我們可以建立本地和遠端視訊元素:

<video id="local" autoplay></video>
<video id="remote" autoplay></video>
複製程式碼

將流分配給video#local元素:

document.querySelector('video#local').srcObject = localStream
複製程式碼

呼叫receiver端,傳遞localStream物件:

const call = peer.call('receiver', localStream)
複製程式碼

接收端的程式碼如下:

peer.on('call', call => {
  call.answer(localStream)
})
複製程式碼

我們也需要得到媒體流。程式碼非常簡單,和傳送端類似,只是我們把所有程式碼包裹在call事件反饋中:

peer.on('call', call => {
  const startChat = async () => {
    const localStream = await navigator.mediaDevices.getUserMedia({
      video: true
    })
    document.querySelector('video#local').srcObject = localStream
    call.answer(localStream)
  }
  startChat()
})
複製程式碼

展示遠端流

我們還需要新增最後一部分到傳送端和接收端中。

一旦從call物件的stream事件中得到遠端流,我們需要將它附加在video#remote元素上。

call.on('stream', remoteStream => {
  document.querySelector('video#remote').srcObject = remoteStream
})
複製程式碼

接收端程式碼如下:

peer.on('call', call => {
  const startChat = async () => {
    const localStream = await navigator.mediaDevices.getUserMedia({
      video: true
    })
    document.querySelector('video#local').srcObject = localStream
    call.answer(localStream)
    call.on('stream', remoteStream => {
      document.querySelector('video#remote').srcObject = remoteStream
    })
  }
  startChat()
})
複製程式碼

傳送端程式碼如下:

const startChat = async () => {
  const localStream = await navigator.mediaDevices.getUserMedia({
    video: true
  })
  document.querySelector('video#local').srcObject = localStream
  const call = peer.call('receiver', localStream)
  call.on('stream', remoteStream => {
    document.querySelector('video#remote').srcObject = remoteStream
  })
}
startChat()
複製程式碼

當其中一端通過導向新網頁或關閉瀏覽器標籤關閉了連線之後,另一端停止接收流,遠端視訊流停止。

總結

我們使用WebRTC建立了一個非常簡單的網路攝像頭通訊App。建立了兩個檔案來處理兩端通訊,但是沒必要這樣做。你可以建立一個使用者介面,允許使用者自己決定是否需要呼叫,更重要的是,他們想與誰通話。可以允許使用者輸入使用者名稱或者從列表中選擇來實現這個功能。

相關文章