如何使用Javascript構建WebRTC視頻直播?

WebRTC是一個免費的開源項目,它經過簡單的API爲瀏覽器和移動應用程序提供實時通訊功能。本文將向你展現WebRTC的基本概念和功能,並指導你使用Node.js構建本身的WebRTC視頻直播。css

先決條件:

  1. 具備Java經驗
  2. 掌握Socket.io基本知識

WebRTC基礎

WebRTC支持在網絡世界中進行實時通訊,主要用於在網絡上傳輸視頻和音頻數據。 在開始編寫代碼以前,咱們首先來看一下WebRTC的最重要概念。html

  1. 信令:

WebRTC用於瀏覽器中的通訊流,但還須要一種機制來協調通訊併發送控制消息,該過程稱爲信令。node

信令用於如下任務:express

  • 初始化和關閉通信
  • 與外界共享網絡配置(IP地址,端口)
  • 報告鏈接錯誤

信令方法不是WebRTC指定的,開發人員能夠自行選擇(本教程將使用Socket.io)。npm

STUN和TURN服務器:

若是主要的WebRTC對等鏈接遇到問題,則將STUN和TURN服務器用做備用方法。 STUN服務器用於獲取計算機的IP地址,而TURN服務器用做對等鏈接失敗的中繼。小程序

既然咱們已經瞭解了WebRTC的基本概念,就能夠繼續開發上面討論的項目。微信小程序

使用Socket.io發出信號

在使用WebRTC經過對等鏈接發送視頻廣播以前,咱們首先須要使用信令方法(在本例中爲Socket.IO)實例化該鏈接。瀏覽器

爲此,咱們建立項目並使用npm安裝所需的依賴項:服務器

mkdir WebSocketsVideoBroadcast && cd WebSocketsVideoBroadcast
npm install express socket.io --save

以後,咱們建立如下文件夾結構:微信

咱們從一個簡單的Socket.io服務器框架開始:

const express = require("express");
const app = express();

const port = 4000;

const http = require("http");
const server = http.createServer(app);

const io = require("socket.io")(server);
app.use(express.static(__dirname + "/public"));

io.sockets.on("error", e => console.log(e));
server.listen(port, () => console.log(`Server is running on port ${port}`));

而後,咱們須要實現客戶端和直播者與服務器的鏈接。 直播者的Socket ID保存到一個變量中,以便咱們之後知道客戶端須要鏈接到的位置。

let broadcaster

io.sockets.on("connection", socket => {
  socket.on("broadcaster", () => {
    broadcaster = socket.id;
    socket.broadcast.emit("broadcaster");
  });
  socket.on("watcher", () => {
    socket.to(broadcaster).emit("watcher", socket.id);
  });
  socket.on("disconnect", () => {
    socket.to(broadcaster).emit("disconnectPeer", socket.id);
  });
});

以後,咱們將實現socket.io事件以初始化WebRTC鏈接。 雙方將使用這些事件來實例化對等鏈接。

socket.on("offer", (id, message) => {
    socket.to(id).emit("offer", socket.id, message);
});
socket.on("answer", (id, message) => {
  socket.to(id).emit("answer", socket.id, message);
});
socket.on("candidate", (id, message) => {
  socket.to(id).emit("candidate", socket.id, message);
});

這就是咱們Socket.io的服務器實現的所有內容,如今咱們能夠繼續進行佈局以及雙方通訊的實現。

Layouts

咱們的佈局由兩個基本HTML文件組成,其中包含一個視頻視圖(稍後將顯示咱們正在發送的視頻流)和一個CSS文件(用於某些基本樣式)。

index.html文件包含一個視頻視圖,該視圖將顯示來自廣播公司的視頻流。 它還會導入socket.io依賴項和咱們的watch.js文件。

<!DOCTYPE html>
<html>
<head>
	<title>Viewer</title>
	<meta charset="UTF-8" />
	<link href="/styles.css" rel="stylesheet">
</head>
<body>
<video playsinline autoplay></video>
<script src="/socket.io/socket.io.js"></script>
<script src="/watch.js"></script>
</body>
</html>

broadcast.html文件與主佈局很是類似,但會導入broadcast.js文件而不是watch.js

<!DOCTYPE html>
<html>
<head>
  <title>Broadcaster</title>
  <meta charset="UTF-8" />
  <link href="/styles.css" rel="stylesheet">
</head>
<body>
<video playsinline autoplay muted></video>
<script src="/socket.io/socket.io.js"></script>
<script src="/broadcast.js"></script>
</body>
</html>

我還爲視頻視圖提供了一些簡單的CSS樣式。

html {
  overflow: hidden;
  height: 100%;
}

video {
  width: 100%;
  height: 100%;
  position: absolute;
  display: block;
  top: 0;
  left: 0;
  object-fit: cover;
}

body {
  background-color: black;
  margin: 0;
  height: 100%;
  width: 100%;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}

RTCPeerConnection

RTCPeerConnections幫助咱們將位於本地網絡中的兩臺計算機相互鏈接。 在談論這些類型的鏈接時,會涉及到不少術語:

  • ICE-互聯網鏈接創建
  • STUN-經過網絡地址轉換器[NAT]進行的用戶數據報協議[UDP]的會話遍歷

因爲當今大多數設備都在NAT路由器後面,所以沒法直接鏈接。 這就是爲何必須由STUN服務器初始化對等鏈接的緣由,STUN服務器將返回咱們能夠鏈接的ICE候選對象。

在本指南中,咱們有兩個不一樣的鏈接部分。 一個是視頻直播方,能夠與客戶端創建多個對等鏈接,並使用流發送視頻。 第二個是客戶端,它與當前視頻直播方只有一個鏈接。

直播方

首先,咱們爲對等鏈接和攝像機建立配置對象。

const peerConnections = {};
const config = {
  iceServers: [
    {
      urls: ["stun:stun.l.google.com:19302"]
    }
  ]
};

const socket = io.connect(window.location.origin);
const video = document.querySelector("video");

// Media contrains
const constraints = {
  video: { facingMode: "user" }
  // Uncomment to enable audio
  // audio: true,
};

咱們使用正式的google STUN服務器進行點對點鏈接,並使用媒體限制條件配置攝像機。 你也能夠經過取消註釋音頻線路來啓用音頻。

在建立對等鏈接以前,咱們首先須要從攝像機獲取視頻,以便將其添加到咱們的鏈接中。

navigator.mediaDevices
  .getUserMedia(constraints)
  .then(stream => {
    video.srcObject = stream;
    socket.emit("broadcaster");
  })
  .catch(error => console.error(error));

接下來,咱們將使用如下代碼建立一個RTCPeerConnection:

socket.on("watcher", id => {
  const peerConnection = new RTCPeerConnection(config);
  peerConnections[id] = peerConnection;

  let stream = video.srcObject;
  stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
    
  peerConnection.onicecandidate = event => {
    if (event.candidate) {
      socket.emit("candidate", id, event.candidate);
    }
  };

  peerConnection
    .createOffer()
    .then(sdp => peerConnection.setLocalDescription(sdp))
    .then(() => {
      socket.emit("offer", id, peerConnection.localDescription);
    });
});

socket.on("answer", (id, description) => {
  peerConnections[id].setRemoteDescription(description);
});

socket.on("candidate", (id, candidate) => {
  peerConnections[id].addIceCandidate(new RTCIceCandidate(candidate));
});

每次有新客戶端加入時,咱們都會建立一個新的RTCPeerConnection並將其保存在咱們的peerConnections對象中。

而後,咱們使用addTrack()方法將本地流添加到鏈接中,並傳遞流和跟蹤數據。

當咱們收到一個ICE候選者時,將調用peerConnection.onicecandidate事件,並將其發送到咱們的服務器。

以後,咱們經過調用peerConnection.createOffer()將鏈接提議發送給客戶端,而後調用peerConnection.setLocalDescription()來配置鏈接。

當客戶端斷開鏈接時,關閉鏈接是應用程序的另外一個重要部分,咱們可使用如下代碼來實現:

socket.on("disconnectPeer", id => {
  peerConnections[id].close();
  delete peerConnections[id];
});

最後,若是用戶關閉窗口,咱們將關閉socket鏈接。

window.onunload = window.onbeforeunload = () => {
  socket.close();
};

 

客戶端

客戶端(觀看視頻的一方))具備幾乎相同的功能。 惟一的區別是,他僅打開了與當前視頻直播方的一個對等鏈接,而且他獲取了視頻,而不是流式傳輸視頻。

咱們還須要爲RTCPeerConnection建立一個配置。

let peerConnection;
const config = {
  iceServers: [
    {
      urls: ["stun:stun.l.google.com:19302"]
    }
  ]
};

const socket = io.connect(window.location.origin);
const video = document.querySelector("video");

而後,咱們能夠建立咱們的RTCPeerConnection並從視頻直播方獲取視頻流。

socket.on("offer", (id, description) => {
  peerConnection = new RTCPeerConnection(config);
  peerConnection
    .setRemoteDescription(description)
    .then(() => peerConnection.createAnswer())
    .then(sdp => peerConnection.setLocalDescription(sdp))
    .then(() => {
      socket.emit("answer", id, peerConnection.localDescription);
    });
  peerConnection.ontrack = event => {
    video.srcObject = event.streams[0];
  };
  peerConnection.onicecandidate = event => {
    if (event.candidate) {
      socket.emit("candidate", id, event.candidate);
    }
  };
});

在這裏,咱們像上面同樣使用配置對象建立了一個新的RTCPeerConnection。 惟一的區別是,咱們調用createAnswer()函數將鏈接應答發送回視頻直播方的請求。

創建鏈接後,咱們能夠繼續使用peerConnection對象的ontrack事件偵聽器獲取視頻流。

咱們還須要爲點對點鏈接實現其餘生命週期功能,這將有助於咱們打開和關閉新鏈接。

socket.on("candidate", (id, candidate) => {
  peerConnection
    .addIceCandidate(new RTCIceCandidate(candidate))
    .catch(e => console.error(e));
});

socket.on("connect", () => {
  socket.emit("watcher");
});

socket.on("broadcaster", () => {
  socket.emit("watcher");
});

window.onunload = window.onbeforeunload = () => {
  socket.close();
  peerConnection.close();
};

至此,該應用程序已完成,能夠繼續在瀏覽器中對其進行測試。

測試應用程序

如今咱們已經完成了該應用程序,是時候對其進行測試,看看它是否能夠工做了。

咱們可使用如下命令啓動該應用程序:

node server.js

該應用程序如今應該在你的localhost:4000上運行,而且能夠經過鏈接到localhost:4000 / broadcast來添加新的視頻直播品程序進行測試。

以後,只須要訪問localhost:4000便可做爲客戶端鏈接到服務器,而且你應該得到從視頻直播方的流式傳輸的視頻。

結論

我但願本文能幫助您瞭解WebRTC的基礎知識以及如何使用它來流式傳輸視頻直播。

EasyRTC視頻會議雲服務

基於WebRTC技術而開發的EasyRTC,是TSINGSEE青犀視頻團隊在音視頻領域多年的技術積累而研發的, 它是覆蓋全球的實時音頻開發平臺,支持一對1、一對多等視頻通話。

WebRTC技術爲何忽然崛起了?

 

EasyRTC擁有MCU和SFU兩種架構,無需安裝客戶端與插件,純H5在線視頻會議系統,支持微信小程序、H5頁面、APP、PC客戶端等接入方式,極大知足語音視頻社交、在線教育和培訓、視頻會議和遠程醫療等場景需求。

隨着移動互聯網的高速發展,AI、5G等等新興技術的到來,結合WebRTC技術,也將衍生出更多的應用場景,改變人類的衣、食、住、行等生活方式。

相關文章
相關標籤/搜索