之前写的使用FFmpeg + Nginx + HLS流媒体播放方案,适合对实时性要求不高的需求,存在延迟,FFmpeg需要将视频流存储到本地文件,而本次方案FFmpeg不需要将视频流存储到本地文件,而是直接将转换后的视频流(如MJPEG格式)通过标准输出(stdout
)传递给WebSocket服务器,WebSocket服务器再将数据实时推送到前端。这种方式是实时流传输,适合需要低延迟的场景。
以下是详细的实现逻辑:
1. FFmpeg 直接输出到 WebSocket 服务器
FFmpeg 通过命令行参数将 RTSP 流转换为 MJPEG 格式,并将输出直接发送到标准输出(stdout
),而不是保存到文件。WebSocket 服务器通过 Node.js 的 child_process
模块捕获 FFmpeg 的输出,并将其转发给连接的客户端。
FFmpeg 命令
ffmpeg -i rtsp://your_rtsp_stream_url -f mjpeg -qscale:v 2 -
-i rtsp://your_rtsp_stream_url
:输入 RTSP 流地址。-f mjpeg
:输出格式为 MJPEG。-qscale:v 2
:设置视频质量(值越小质量越高)。-
:将输出发送到标准输出(stdout
),而不是文件。
2. WebSocket 服务器捕获 FFmpeg 输出
WebSocket 服务器通过 Node.js 启动 FFmpeg 进程,并监听其 stdout
数据流。每当 FFmpeg 输出一帧数据时,WebSocket 服务器就将这帧数据发送给所有连接的客户端。
WebSocket 服务器代码 (server.js
)
const { spawn } = require('child_process');
const WebSocket = require('ws');
// 创建 WebSocket 服务器
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('Client connected');
// 启动 FFmpeg 进程
const ffmpeg = spawn('ffmpeg', [
'-rtsp_transport', 'tcp', // 使用 TCP 传输 RTSP 流(如果 UDP 不稳定)
'-i', 'rtsp://admin:password@192.168.1.60:554/Streaming/Channels/101/', // RTSP 流地址
'-f', 'mpegts', // 输出格式为 MPEG-TS
'-codec:v', 'mpeg1video', // 使用 MPEG-1 编码
'-s', '640x360', // 分辨率
'-b:v', '800k', // 视频比特率
'-bf', '0', // 禁用 B 帧
'-an', // 禁用音频
'pipe:1' // 输出到标准输出
]);
// 将 FFmpeg 的输出发送到 WebSocket 客户端
ffmpeg.stdout.on('data', (data) => {
if (ws.readyState === ws.OPEN) {
ws.send(data); // 发送二进制数据
}
});
// 处理 FFmpeg 的错误输出
ffmpeg.stderr.on('data', (data) => {
console.error(`FFmpeg error: ${data}`);
});
// 处理 FFmpeg 进程退出
ffmpeg.on('close', (code) => {
console.log(`FFmpeg process exited with code ${code}`);
});
// 客户端断开连接时关闭 FFmpeg 进程
ws.on('close', () => {
console.log('Client disconnected');
ffmpeg.kill(); // 杀死 FFmpeg 进程
});
});
console.log('WebSocket server is running on ws://localhost:8080');
3. 前端使用 JsMpeg 播放视频流
前端通过 JsMpeg 库连接到 WebSocket 服务器,并实时解码和播放 MJPEG 视频流。
前端代码 (index.html
)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video Streaming</title>
<script src="https://cdn.jsdelivr.net/npm/jsmpeg/jsmpeg.min.js"></script>
</head>
<body>
<canvas id="video-canvas"></canvas>
<script>
// 创建 JsMpeg 播放器
const canvas = document.getElementById('video-canvas');
const player = new JSMpeg.Player('ws://localhost:8080', {
canvas: canvas
});
</script>
</body>
</html>
4. 运行流程
-
FFmpeg:
- 从 RTSP 流中读取视频数据。
- 将视频数据转换为 MJPEG 格式。
- 将转换后的数据通过标准输出(
stdout
)发送。
-
WebSocket 服务器:
- 启动 FFmpeg 进程并捕获其
stdout
数据。 - 将捕获到的数据通过 WebSocket 发送给前端。
- 启动 FFmpeg 进程并捕获其
-
前端:
- 使用 JsMpeg 连接到 WebSocket 服务器。
- 接收并解码 MJPEG 数据,实时显示在
<canvas>
中。
5. 优点
- 实时性:视频流直接从 FFmpeg 推送到前端,延迟较低。
- 无需存储:视频流不需要保存到本地文件,节省磁盘空间。
- 简单易用:只需运行 FFmpeg 和 WebSocket 服务器即可。
6. 注意事项
- FFmpeg 性能:如果 RTSP 流的分辨率较高,FFmpeg 的转换可能会占用较多 CPU 资源。可以通过调整
-qscale:v
参数来优化性能。 - 网络带宽:MJPEG 格式的视频流数据量较大,确保网络带宽足够。
- WebSocket 连接数:如果有多个客户端连接,WebSocket 服务器需要处理更多的数据转发,可能会增加服务器负载。
7. 总结
通过这种方式,FFmpeg 直接将 RTSP 流转换为 MJPEG 格式并输出到 WebSocket 服务器,WebSocket 服务器再将数据实时推送到前端,前端使用 JsMpeg 进行播放。整个过程无需存储视频文件,适合实时视频流传输的场景。