本文在我们的《 6个JavaScript项目》 一书中有介绍 。 构建使您对现代JavaScript开发非常熟悉的项目。
随着WebRTC的出现以及浏览器实时处理对等通信的能力的增强,构建实时应用程序比以往任何时候都更加容易。 在本教程中,我们将介绍SimpleWebRTC以及如何在实现WebRTC时使我们的生活更轻松。 在整篇文章中,我们将构建一个具有消息传递功能的WebRTC视频聊天应用程序。
如果您需要一些有关WebRTC和对等通信的背景知识,我建议阅读WebRTC的曙光和getUserMedia API简介 。
什么是SimpleWebRTC
在继续之前,了解我们将要使用的主要工具很重要。 SimpleWebRTC是一个JavaScript库,可简化WebRTC对等数据,视频和音频呼叫。
SimpleWebRTC充当浏览器WebRTC实现的包装。 您可能已经知道,浏览器供应商并不完全同意采用一种实现不同功能的方式,这意味着对于每种浏览器,WebRTC都有不同的实现。 作为开发人员,您必须为计划支持的每种浏览器编写不同的代码。 SimpleWebRT充当该代码的包装器。 它公开的API易于使用和理解,这使其成为实现跨浏览器WebRTC的绝佳选择。
构建WebRTC视频聊天应用
现在是时候通过构建应用程序来使我们动手了。 我们将构建一个在Express服务器之上运行的单页应用程序。
请注意,您可以从我们的GitHub存储库下载本教程的代码。 要运行它或在家中跟随,您需要安装Node和npm。 如果您不熟悉这些内容,或者想获得安装它们的帮助,请查看我们以前的教程:
您还需要一台具有网络摄像头的PC或笔记本电脑。 如果没有,则需要为自己准备一个USB网络摄像头,该摄像头可以连接到显示器的顶部。 您可能需要一个朋友或另一台设备来测试远程连接。
依存关系
我们将使用以下依赖项来构建我们的项目:
- SimpleWebRTC — WebRTC库
- 语义UI CSS —一个优雅的CSS框架
- jQuery —用于选择页面上的元素和事件处理。
- 把手 -一个JavaScript模板库,我们将使用它来为消息生成HTML
- Express — NodeJS服务器。
项目设置
转到您的工作区并创建一个文件夹simplewebrtc-messenger
。 在VSCode或您喜欢的编辑器中打开文件夹,然后创建以下文件和文件夹结构:
simplewebrtc-messenger
├── public
│ ├── images
│ │ └── image.png
│ ├── index.html
│ └── js
│ └── app.js
├── README.md
└── server.js
或者,如果您愿意,也可以通过命令行执行相同的操作:
mkdir -p simplewebrtc-messenger/public/{images,js}
cd simplewebrtc-messenger
touch public/js/app.js public/index.html .gitignore README.md server.js
打开README.md
并复制以下内容:
# Simple WebRTC Messenger
A tutorial on building a WebRTC video chat app using SimpleWebRTC.
如果您打算使用git存储库,请将行node_modules
添加到.gitignore
文件。 使用以下命令生成package.json
文件:
npm init -y
您应该获得以下输出:
{
"name": "simplewebrtc-messenger",
"version": "1.0.0",
"description": "A tutorial on building a WebRTC video chat app using SimpleWebRTC.",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
现在让我们安装依赖项:
npm install express handlebars jquery semantic-ui-css simplewebrtc
随着安装的进行,将此代码复制到server.js
:
const express = require('express');
const app = express();
const port = 3000;
// Set public folder as root
app.use(express.static('public'));
// Provide access to node_modules folder from the client-side
app.use('/scripts', express.static(`${__dirname}/node_modules/`));
// Redirect all traffic to index.html
app.use((req, res) => res.sendFile(`${__dirname}/public/index.html`));
app.listen(port, () => {
console.info('listening on %d', port);
});
服务器代码是非常标准的。 只需阅读评论以了解发生了什么。
接下来,让我们设置我们的public/index.html
文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="scripts/semantic-ui-css/semantic.min.css">
<title>SimpleWebRTC Demo</title>
<style>
html { margin-top: 20px; }
#chat-content { height: 180px; overflow-y: scroll; }
</style>
</head>
<body>
<!-- Main Content -->
<div class="ui container">
<h1 class="ui header">Simple WebRTC Messenger</h1>
<hr>
</div>
<!-- Scripts -->
<script src="scripts/jquery/dist/jquery.min.js"></script>
<script src="scripts/semantic-ui-css/semantic.min.js"></script>
<script src="scripts/handlebars/dist/handlebars.min.js "></script>
<script src="scripts/simplewebrtc/out/simplewebrtc-with-adapter.bundle.js"></script>
<script src="js/app.js"></script>
</body>
</html>
接下来,让我们设置基本的客户端JavaScript代码。 将此代码复制到public/js/app.js
:
window.addEventListener('load', () => {
// Put all client-side code here
});
最后,从我们的GitHub存储库下载此图像 ,并将其保存在public/images
文件夹中。
现在我们可以运行我们的应用程序:
npm start
在浏览器中打开URL localhost:3000 ,您应该看到以下内容:
标记
现在让我们在public/index.html
上工作。 为了简单起见(尤其是如果您已经熟悉Handlebars),您可以从GitHub存储库中复制整个标记代码。 否则,让我们逐步进行操作。 首先,复制此代码并将其放在ui container
div中的<hr>
标记之后:
<div class="ui two column stackable grid">
<!-- Chat Section -->
<div class="ui ten wide column">
<div class="ui segment">
<!-- Chat Room Form -->
<div class="ui form">
<div class="fields">
<div class="field">
<label>User Name</label>
<input type="text" placeholder="Enter user name" id="username" name="username">
</div>
<div class="field">
<label>Room</label>
<input type="text" placeholder="Enter room name" id="roomName" name="roomName">
</div>
</div>
<br>
<div class="ui buttons">
<div id="create-btn" class="ui submit orange button">Create Room</div>
<div class="or"></div>
<div id="join-btn" class="ui submit green button">Join Room</div>
</div>
</div>
<!-- Chat Room Messages -->
<div id="chat"></div>
</div>
</div>
<!-- End of Chat Section -->
<!-- Local Camera -->
<div class="ui six wide column">
<h4 class="ui center aligned header" style="margin:0;">
Local Camera
</h4>
<img id="local-image" class="ui large image" src="images/image.png">
<video id="local-video" class="ui large image hidden" autoplay></video>
</div>
</div>
<!-- Remote Cameras -->
<h3 class="ui center aligned header">Remote Cameras</h3>
<div id="remote-videos" class="ui stackable grid">
<div class="four wide column">
<img class="ui centered medium image" src="images/image.png">
</div>
<div class="four wide column">
<img class="ui centered medium image" src="images/image.png">
</div>
<div class="four wide column">
<img class="ui centered medium image" src="images/image.png">
</div>
<div class="four wide column">
<img class="ui centered medium image" src="images/image.png">
</div>
</div>
浏览标记代码并阅读注释,以了解每个部分的用途。 如果您不熟悉CSS库,还可以查看Semantic UI文档。 刷新浏览器。 您应该具有以下视图:
我们使用空白图像作为占位符,以指示摄像头位置将在网页上流到的位置。 请注意,只要您的互联网带宽可以处理此应用,它将能够支持多个远程连接。
范本
现在,让我们添加三个Handlebar模板,这些模板将使我们的网页具有交互性。
在ui container
div之后放置以下标记(尽管位置并不重要)。 我们将从聊天容器开始,该容器由以下部分组成:
- 房间编号
- 空的聊天消息容器(稍后将通过JavaScript填充)
- 用于发布消息的输入。
<!-- Chat Template -->
<script id="chat-template" type="text/x-handlebars-template">
<h3 class="ui orange header">Room ID -> <strong>{{ room }}</strong></h3>
<hr>
<div id="chat-content" class="ui feed"> </div>
<hr>
<div class="ui form">
<div class="ui field">
<label>Post Message</label>
<textarea id="post-message" name="post-message" rows="1"></textarea>
</div>
<div id="post-btn" class="ui primary submit button">Send</div>
</div>
</script>
接下来,添加以下模板,该模板将用于显示用户聊天消息:
<!-- Chat Content Template -->
<script id="chat-content-template" type="text/x-handlebars-template">
{{#each messages}}
<div class="event">
<div class="label">
<i class="icon blue user"></i>
</div>
<div class="content">
<div class="summary">
<a href="#"> {{ username }}</a> posted on
<div class="date">
{{ postedOn }}
</div>
</div>
<div class="extra text">
{{ message }}
</div>
</div>
</div>
{{/each}}
</script>
最后,添加以下模板,该模板将用于显示来自远程摄像机的流:
<!-- Remote Video Template -->
<script id="remote-video-template" type="text/x-handlebars-template">
<div id="{{ id }}" class="four wide column"></div>
</script>
标记代码很可能是不言自明的,因此让我们继续为我们的应用程序编写客户端JavaScript代码。
主应用脚本
打开文件public/js/app.js
并添加以下代码:
// Chat platform
const chatTemplate = Handlebars.compile($('#chat-template').html());
const chatContentTemplate = Handlebars.compile($('#chat-content-template').html());
const chatEl = $('#chat');
const formEl = $('.form');
const messages = [];
let username;
// Local Video
const localImageEl = $('#local-image');
const localVideoEl = $('#local-video');
// Remote Videos
const remoteVideoTemplate = Handlebars.compile($('#remote-video-template').html());
const remoteVideosEl = $('#remote-videos');
let remoteVideosCount = 0;
// Add validation rules to Create/Join Room Form
formEl.form({
fields: {
roomName: 'empty',
username: 'empty',
},
});
在这里,我们正在初始化计划要操纵的几个元素。 我们还向表单添加了验证规则,以使用户不能将两个字段都留为空白。
接下来,让我们初始化WebRTC代码:
// create our WebRTC connection
const webrtc = new SimpleWebRTC({
// the id/element dom element that will hold "our" video
localVideoEl: 'local-video',
// the id/element dom element that will hold remote videos
remoteVideosEl: 'remote-videos',
// immediately ask for camera access
autoRequestMedia: true,
});
// We got access to local camera
webrtc.on('localStream', () => {
localImageEl.hide();
localVideoEl.show();
});
现在您知道了为什么将其称为SimpleWebRTC。 这就是初始化WebRTC代码所需要做的。 注意,我们甚至都没有指定任何ICE服务器或STUN服务器。 它只是工作。 但是,您可以使用其他TURN服务,例如Xirsys 。 您需要设置一个本地SignalMaster服务器来处理WebRTC信令。
让我们快速刷新网页以确认新代码是否正常工作:
该页面应请求访问您的相机和麦克风。 只需单击“ 接受” ,您将获得上面的视图。
聊天室脚本
现在,让表单起作用。 我们需要编写用于创建和加入房间的逻辑。 另外,我们需要编写其他逻辑来显示聊天室。 我们将为此使用chat-room-template
。 首先,将点击处理程序附加到表单的按钮上:
$('.submit').on('click', (event) => {
if (!formEl.form('is valid')) {
return false;
}
username = $('#username').val();
const roomName = $('#roomName').val().toLowerCase();
if (event.target.id === 'create-btn') {
createRoom(roomName);
} else {
joinRoom(roomName);
}
return false;
});
接下来,我们需要声明createRoom
和joinRoom
函数。 将以下代码放在点击处理程序代码之前:
// Register new Chat Room
const createRoom = (roomName) => {
console.info(`Creating new room: ${roomName}`);
webrtc.createRoom(roomName, (err, name) => {
showChatRoom(name);
postMessage(`${username} created chatroom`);
});
};
// Join existing Chat Room
const joinRoom = (roomName) => {
console.log(`Joining Room: ${roomName}`);
webrtc.joinRoom(roomName);
showChatRoom(roomName);
postMessage(`${username} joined chatroom`);
};
创建或加入房间很简单:只需使用SimpleWebRTC的createRoom和joinRoom方法 。
您可能还已经注意到,我们具有尚未定义的showChatroom
和postMessage
函数。 现在,通过在调用代码之前插入以下代码来做到这一点:
// Post Local Message
const postMessage = (message) => {
const chatMessage = {
username,
message,
postedOn: new Date().toLocaleString('en-GB'),
};
// Send to all peers
webrtc.sendToAll('chat', chatMessage);
// Update messages locally
messages.push(chatMessage);
$('#post-message').val('');
updateChatMessages();
};
// Display Chat Interface
const showChatRoom = (room) => {
// Hide form
formEl.hide();
const html = chatTemplate({ room });
chatEl.html(html);
const postForm = $('form');
// Post Message Validation Rules
postForm.form({
message: 'empty',
});
$('#post-btn').on('click', () => {
const message = $('#post-message').val();
postMessage(message);
});
$('#post-message').on('keyup', (event) => {
if (event.keyCode === 13) {
const message = $('#post-message').val();
postMessage(message);
}
});
};
花一些时间浏览代码以了解逻辑。 您很快就会遇到另一个我们尚未声明的函数updateChatMessages
。 现在添加它:
// Update Chat Messages
const updateChatMessages = () => {
const html = chatContentTemplate({ messages });
const chatContentEl = $('#chat-content');
chatContentEl.html(html);
// automatically scroll downwards
const scrollHeight = chatContentEl.prop('scrollHeight');
chatContentEl.animate({ scrollTop: scrollHeight }, 'slow');
};
该功能的目的仅仅是用新消息更新聊天UI。 我们还需要一个功能来接受来自远程用户的消息。 将以下函数添加到app.js
:
// Receive message from remote user
webrtc.connection.on('message', (data) => {
if (data.type === 'chat') {
const message = data.payload;
messages.push(message);
updateChatMessages();
}
});
这就是让聊天室正常工作所需要的全部逻辑。 刷新页面并登录:
点击创建房间按钮。 您将被带到此视图。 发布一些消息以确认聊天室正在运行。
确认它可以正常工作后,继续执行下一个任务。
远程摄像机
如前所述,SimpleWebRTC支持多个对等方。 这是当新用户加入会议室时添加远程视频流的代码:
// Remote video was added
webrtc.on('videoAdded', (video, peer) => {
const id = webrtc.getDomId(peer);
const html = remoteVideoTemplate({ id });
if (remoteVideosCount === 0) {
remoteVideosEl.html(html);
} else {
remoteVideosEl.append(html);
}
$(`#${id}`).html(video);
$(`#${id} video`).addClass('ui image medium'); // Make video element responsive
remoteVideosCount += 1;
});
而已。 抱歉,如果您期望更复杂的事情。 我们所做的只是为videoAdded
添加事件侦听videoAdded
,该事件侦听器的回调接收可以直接添加到DOM的video
元素。 它还会收到一个peer
对象,该对象包含有关我们的对等连接的有用信息,但是在这种情况下,我们只对DOM元素的ID感兴趣。
不幸的是,如果不在HTTPS服务器上运行,就无法测试这部分代码。 从理论上讲,您可以为Express服务器生成一个自签名证书,以便在内部网络中运行该应用程序。 但坏消息是,如果证书不是来自受信任的机构,浏览器将不允许您访问网络摄像头。
测试上述代码的最简单解决方案是将其部署到支持HTTPS协议的公共服务器上。
部署方式
我们将要执行的此方法是部署NodeJS应用的最简单方法之一。 我们要做的就是首先在now.sh中注册一个帐户。
只需选择免费计划。 您需要提供您的电子邮件地址。 您还需要验证电子邮件地址才能激活帐户。 接下来, now
在系统上安装CLI工具:
npm install -g now
安装完成后,您可以部署应用程序。 只需在项目文件夹的根目录下执行以下命令:
now --public
如果这是您第一次运行该命令,系统将要求您输入电子邮件地址。 然后,您会收到一封电子邮件,您需要该电子邮件来验证您的登录信息。 验证完成后,您now --public
再次执行命令now --public
。 几秒钟后,您的应用将在指定的URL上启动并运行,该URL将在终端上打印出来。
如果您使用的是VSCode集成终端,只需按ALT ,然后单击以在浏览器中打开URL。
您需要允许该页面访问您的相机和麦克风。 接下来,像以前一样创建一个房间。 登录后,您需要访问另一台设备,例如另一台带有前置摄像头的笔记本电脑或智能手机。 您也可以要求有Internet连接的朋友帮助您。 只需访问相同的URL,然后输入新的用户名和相同的房间名称。 远程用户将必须点击“ 加入会议室”按钮。 在几秒钟内,两个设备都应连接到聊天室。 如果设备没有摄像头,那就可以了,因为聊天功能仍然可以使用。
结论
在本教程中,您了解了SimpleWebRTC以及如何使用它来创建实时应用程序。 具体来说,我们创建了一个消息传递应用程序,允许用户发送文本并与远程对等方进行视频通话。 SimpleWebRTC是一个非常出色的跨浏览器库,用于在Web应用程序中轻松实现WebRTC。
别忘了本教程中使用的代码在GitHub上可用 。 克隆它,制作一些有趣的东西,然后玩得开心!
From: https://www.sitepoint.com/webrtc-video-chat-application-simplewebrtc/