(1)第一步
首先创建一个信令服务器
需要安装
express
socket.io
npm i express socket.io
在根目录下创建 index.js
信令服务器
index.js:
var express = require('express'); // web 框架
const fs = require('fs'); // 文件读取
var app = express(); // 站点
app.use("/", express.static("public")); // 开放 public 目录
// 证书配置,web 调起摄像头需要 https
let options = {
key: fs.readFileSync('./ssl/privatekey.pem'), // 证书文件的存放目录
cert: fs.readFileSync('./ssl/certificate.pem')
}
const https = require('https').Server(options, app); // 创建 https 服务器
const io = require('socket.io')(https);
io.on('connection', (socket) => {
// 新连接
socket.on('conn', function (userName) {
socket.join('room'); // 加入房间
socket.emit('conn', userName);
console.log('新用户:' + userName);
});
// 接收 Offer 信令并发送给其他连接
socket.on('signalOffer', function (message) {
socket.to('room').emit('signalOffer', message);
});
// 接收 Answer 信令
socket.on('signalAnswer', function (message) {
socket.to('room').emit('signalAnswer', message);
});
// 接收 iceOffer
socket.on('iceOffer', function (message) {
socket.to('room').emit('iceOffer', message);
});
// 接收 iceAnswer
socket.on('iceAnswer', function (message) {
socket.to('room').emit('iceAnswer', message);
});
});
const config = {
port: 8101 // 启用端口
};
https.listen(config.port); // 启动服务
console.log('https listening on ' + config.port);
启动服务器:node index.js
(2)第二步
在 /public
目录下创建 index.html 通话页面
/public/index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>音视频通话</title>
</head>
<body>
<div class="container">
<h1>音视频通话</h1>
<hr>
<div class="video_container" align="center">
<!-- 本地视频流 -->
<video id="local_video" controls autoplay muted webkit-playsinline></video>
<!-- 远端视频流 -->
<video id="remote_video" controls autoplay muted webkit-playsinline></video>
</div>
<hr>
<button id="startButton">加入房间</button>
<button id="hangupButton">挂断</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.4.0/socket.io.min.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="main.js"></script>
</div>
</body>
</html>
(3)第三步
创建 main.js (WebRTC关键代码)
/public/main.js:
'use strict'
var localVideo = document.getElementById('local_video'); // 本地视频 Video
var remoteVideo = document.getElementById('remote_video'); // 远端视频 Video
var startButton = document.getElementById('startButton'); // 加入房间按钮
var hangupButton = document.getElementById('hangupButton'); // 挂断按钮
var pc; // RTCPeerConnection 实例(WebRTC 连接实例)
var localStream; // 本地视频流
var socket = io.connect(); // 创建 socket 连接
// ice 打洞服务器
var config = {
'iceServers': [{
'urls': 'stun:stun.l.google.com:19302'
}]
};
// offer 配置
const offerOptions = {
offerToReceiveVideo: 1,
offerToReceiveAudio: 1
};
hangupButton.disabled = true;
startButton.addEventListener('click', startAction);
hangupButton.addEventListener('click', hangupAction);
// 点击加入房间
function startAction () {
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(function (mediastream) {
localStream = mediastream; // 本地视频流
localVideo.srcObject = mediastream; // 播放本地视频流
startButton.disabled = true;
socket.emit('conn', 'room'); // 连接 socket
}).catch(function (e) {
console.log(JSON.stringify(e));
});
}
// socket 连接成功
socket.on('conn', function (room, id) {
hangupButton.disabled = false;
pc = new RTCPeerConnection(config); // 创建 RTC 连接
localStream.getTracks().forEach(track => pc.addTrack(track, localStream)); // 添加本地视频流 track
// 创建 Offer 请求
pc.createOffer(offerOptions).then(function (offer) {
pc.setLocalDescription(offer); // 设置本地 Offer 描述,(设置描述之后会触发ice事件)
socket.emit('signalOffer', offer); // 发送 Offer 请求信令
});
// 监听 ice
pc.addEventListener('icecandidate', function (event) {
var iceCandidate = event.candidate;
if (iceCandidate) {
// 发送 iceOffer 请求
socket.emit('iceOffer', iceCandidate);
}
});
});
// 接收 Offer 请求信令
socket.on('signalOffer', function (message) {
pc.setRemoteDescription(new RTCSessionDescription(message)); // 设置远端描述
// 创建 Answer 请求
pc.createAnswer().then(function (answer) {
pc.setLocalDescription(answer); // 设置本地 Answer 描述
socket.emit('signalAnswer', answer); // 发送 Answer 请求信令
})
// 监听远端视频流
pc.addEventListener('addstream', function (event) {
remoteVideo.srcObject = event.stream; // 播放远端视频流
});
});
// 接收 Answer 请求信令
socket.on('signalAnswer', function (message) {
pc.setRemoteDescription(new RTCSessionDescription(message)); // 设置远端描述
console.log('remote answer');
// 监听远端视频流
pc.addEventListener('addstream', function (event) {
remoteVideo.srcObject = event.stream;
});
});
// 接收 iceOffer
socket.on('iceOffer', function (message) {
addIceCandidates(message)
});
// 接收 iceAnswer
socket.on('iceAnswer', function (message) {
addIceCandidates(message)
});
// 添加 IceCandidate
function addIceCandidates (message) {
if (pc !== 'undefined') {
pc.addIceCandidate(new RTCIceCandidate(message));
}
}
// 挂断
function hangupAction () {
localStream.getTracks().forEach(track => track.stop());
pc.close();
pc = null;
hangupButton.disabled = true;
startButton.disabled = false;
}
(4)在局域网内开启音视频通话
在浏览器输入地址 https://192.168.0.100:8101
本机可以输入 https://localhost:8101
如遇提示 您的连接不是私密连接
请继续点击 高级
=> 继续前往192.168.0.100(不安全)
点击 继续前往
通话页面:
点击 加入房间
ios手机端画面:
在 ios safari 浏览器中,视频流不会自动播放,需要点击播放按钮
使用两只手指缩放到页面上进行播放:
android手机端画面:
在 android 浏览器中,视频流可以自动播放,请尽量使用高版本,以避免兼容性问题
当前代码只支持1对1模式
源码点击