本文将向您展示 WebRTC 的基本概念和功能,并指导您使用 Node.js 构建您自己的 WebRTC 视频直播。
WebRTC 是一个免费的开源项目,它通过简单的 API 为浏览器和移动应用程序提供实时通信功能。
本文将向您展示 WebRTC 的基本概念和功能,并指导您使用 Node.js 构建您自己的 WebRTC 视频直播。
先决条件
- 使用 Javascript 的经验
- 基本的 Socket.io 知识
WebRTC 基础知识
WebRTC 实现了网络世界的实时通信,主要用于传输网络上的视频和音频数据。在进入代码之前,让我们先看看 WebRTC 最重要的概念。
信令:
WebRTC 用于浏览器中的通信流,但也需要一种机制来协调通信和发送控制消息,这一过程称为信令。
信令用于以下任务:
- 初始化和关闭通信
- 与外界共享网络配置(IP 地址、端口)
- 报告连接错误
WebRTC 未指定信令方法,开发人员可以自行选择(本教程将使用 Socket.io)。
STUN 和 TURN 服务器:
STUN 和 TURN 服务器用作备用方法,以防主要的 WebRTC 对等连接出现问题。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}`));
然后我们需要实现客户端和广播者到服务器的连接。广播者的套接字 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 服务器实现的全部,现在我们可以继续布局以及我们的观察者和直播者实现。
布局
我们的布局由两个基本的 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;
}
RTCP 对等连接
RTCPeerConnections 帮助我们将位于本地网络中的两台计算机相互连接起来。在谈论这些类型的连接时,您会经常听到一些术语:
- ICE – 互联网连接建立
- STUN – 用户数据报协议 [UDP] 通过网络地址转换器 [NAT] 的会话遍历
由于现在大多数设备都在 NAT 路由器后面,因此无法直接连接。这就是为什么对等连接必须由 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];
});
最后,如果用户关闭窗口,我们将关闭套接字连接。
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 以作为客户端连接到服务器,您就应该获得从直播公司流式传输的视频。
这个应用程序的完整代码也可以在我的Github上找到。该存储库还包含一些修改,例如选择您从中流式传输的相机的能力。
结论
你一路走到最后!我希望本文能帮助您了解 WebRTC 的基础知识以及如何使用它来流式传输视频直播。
作者:Gabriel Tanner
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/webrtc/14906.html