簡單視訊通話應用
WebSocket與Node.js實現視訊通話應用教學
目錄
- 專案初始化
- 服務器端設置
- 客戶端實現
- WebRTC整合
- 部署說明
1. 專案初始化
首先建立專案目錄並初始化:
mkdir video-chat-app
cd video-chat-app
npm init -y
安裝必要的依賴:
npm install express ws socket.io
npm install --save-dev nodemon
2. 服務器端設置
2.1 建立基本服務器
創建 server.js
:
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const path = require('path');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
app.use(express.static(path.join(__dirname, 'public')));
io.on('connection', (socket) => {
console.log('用戶連接:', socket.id);
// 處理加入房間
socket.on('join-room', (roomId, userId) => {
socket.join(roomId);
socket.to(roomId).emit('user-connected', userId);
socket.on('disconnect', () => {
socket.to(roomId).emit('user-disconnected', userId);
});
});
// 處理信令
socket.on('offer', (offer, roomId) => {
socket.to(roomId).emit('offer', offer);
});
socket.on('answer', (answer, roomId) => {
socket.to(roomId).emit('answer', answer);
});
socket.on('ice-candidate', (candidate, roomId) => {
socket.to(roomId).emit('ice-candidate', candidate);
});
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`服務器運行在端口 ${PORT}`);
});
3. 客戶端實現
3.1 HTML結構
創建 public/index.html
:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>視訊通話應用</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<div class="video-container">
<video id="localVideo" autoplay playsinline muted></video>
<video id="remoteVideo" autoplay playsinline></video>
</div>
<div class="controls">
<button id="startButton">開始通話</button>
<button id="muteButton">靜音</button>
<button id="videoButton">關閉視訊</button>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="client.js"></script>
</body>
</html>
3.2 客戶端JavaScript
創建 public/client.js
:
const socket = io('/');
let localStream;
let remoteStream;
let peerConnection;
const configuration = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
};
// 獲取HTML元素
const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
const startButton = document.getElementById('startButton');
const muteButton = document.getElementById('muteButton');
const videoButton = document.getElementById('videoButton');
// 生成隨機房間ID
const roomId = Math.random().toString(36).substring(7);
// 初始化視訊
async function initializeStream() {
try {
localStream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
localVideo.srcObject = localStream;
// 創建RTCPeerConnection
createPeerConnection();
// 加入房間
socket.emit('join-room', roomId, socket.id);
} catch (error) {
console.error('獲取媒體設備失敗:', error);
}
}
// 創建對等連接
function createPeerConnection() {
peerConnection = new RTCPeerConnection(configuration);
// 添加本地流
localStream.getTracks().forEach(track => {
peerConnection.addTrack(track, localStream);
});
// 處理遠端流
peerConnection.ontrack = event => {
remoteVideo.srcObject = event.streams[0];
};
// 處理ICE候選者
peerConnection.onicecandidate = event => {
if (event.candidate) {
socket.emit('ice-candidate', event.candidate, roomId);
}
};
}
// 創建提議
async function createOffer() {
try {
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
socket.emit('offer', offer, roomId);
} catch (error) {
console.error('創建提議失敗:', error);
}
}
// 創建應答
async function createAnswer(offer) {
try {
await peerConnection.setRemoteDescription(offer);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
socket.emit('answer', answer, roomId);
} catch (error) {
console.error('創建應答失敗:', error);
}
}
// Socket.io事件處理
socket.on('user-connected', userId => {
console.log('用戶已連接:', userId);
createOffer();
});
socket.on('offer', offer => {
createAnswer(offer);
});
socket.on('answer', answer => {
peerConnection.setRemoteDescription(answer);
});
socket.on('ice-candidate', candidate => {
peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
});
socket.on('user-disconnected', userId => {
console.log('用戶已斷開連接:', userId);
});
// 按鈕事件處理
startButton.addEventListener('click', initializeStream);
muteButton.addEventListener('click', () => {
const audioTrack = localStream.getAudioTracks()[0];
audioTrack.enabled = !audioTrack.enabled;
muteButton.textContent = audioTrack.enabled ? '靜音' : '取消靜音';
});
videoButton.addEventListener('click', () => {
const videoTrack = localStream.getVideoTracks()[0];
videoTrack.enabled = !videoTrack.enabled;
videoButton.textContent = videoTrack.enabled ? '關閉視訊' : '開啟視訊';
});
3.3 CSS樣式
創建 public/style.css
:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #f0f2f5;
}
.container {
max-width: 1200px;
margin: 20px auto;
padding: 20px;
}
.video-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
video {
width: 100%;
max-width: 600px;
border-radius: 8px;
background-color: #000;
}
.controls {
display: flex;
justify-content: center;
gap: 10px;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
background-color: #0084ff;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #0066cc;
}
#muteButton, #videoButton {
background-color: #666;
}
#muteButton:hover, #videoButton:hover {
background-color: #444;
}
4. WebRTC整合
4.1 信令處理
WebRTC需要通過信令服務器來交換連接所需的信息。主要包括:
- 會話描述協議(SDP)交換
- ICE候選者交換
- 連接狀態管理
4.2 STUN/TURN服務器配置
在生產環境中,建議使用TURN服務器來處理NAT穿透問題:
const configuration = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{
urls: 'turn:your-turn-server.com',
username: 'username',
credential: 'password'
}
]
};
5. 部署說明
5.1 本地開發
-
安裝依賴:
npm install
-
啟動開發服務器:
npm run dev
5.2 生產環境部署
-
確保設置了正確的環境變數:
PORT=3000 TURN_SERVER_URL=your-turn-server.com TURN_USERNAME=username TURN_PASSWORD=password
-
啟動應用:
npm start
5.3 安全性考慮
- 使用HTTPS
- 實施用戶認證
- 限制房間人數
- 添加錯誤處理機制
使用說明
- 開啟應用後,點擊"開始通話"按鈕
- 允許瀏覽器使用攝像頭和麥克風
- 分享房間ID給其他用戶
- 使用控制按鈕管理音訊和視訊
注意事項
- 確保瀏覽器支援WebRTC
- 使用現代瀏覽器以獲得最佳體驗
- 確保有穩定的網路連接
- 在生產環境中使用TURN服務器
擴展建議
- 添加聊天功能
- 實現螢幕分享
- 添加房間管理
- 實現錄製功能
- 添加畫質控制選項
以上就是使用WebSocket和Node.js實現視訊通話的基本教學。這個實現提供了基礎的一對一視訊通話功能,您可以根據需求進行擴展和優化。